Completed
Push — add/sync-rest-comments ( 1da85c )
by
unknown
09:46
created

WPCOM_JSON_API_Site_Settings_Endpoint::callback()   C

Complexity

Conditions 8
Paths 11

Size

Total Lines 35
Code Lines 17

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 35
rs 5.3846
cc 8
eloc 17
nc 11
nop 2
1
<?php
2
3
class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
4
5
	public static $site_format = array(
6
 		'ID'                => '(int) Site ID',
7
 		'name'              => '(string) Title of site',
8
 		'description'       => '(string) Tagline or description of site',
9
 		'URL'               => '(string) Full URL to the site',
10
		'lang'              => '(string) Primary language code of the site',
11
		'settings'          => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.',
12
	);
13
14
	// GET /sites/%s/settings
15
	// POST /sites/%s/settings
16
	function callback( $path = '', $blog_id = 0 ) {
17
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
18
		if ( is_wp_error( $blog_id ) ) {
19
			return $blog_id;
20
		}
21
22
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
23
			$this->load_theme_functions();
24
		}
25
26
		if ( ! is_user_logged_in() ) {
27
			return new WP_Error( 'Unauthorized', 'You must be logged-in to manage settings.', 401 );
28
		} else if ( ! current_user_can( 'manage_options' ) ) {
29
			return new WP_Error( 'Forbidden', 'You do not have the capability to manage settings for this site.', 403 );
30
		}
31
32
		if ( 'GET' === $this->api->method ) {
33
			/**
34
			 * Fires on each GET request to a specific endpoint.
35
			 *
36
			 * @module json-api
37
			 *
38
			 * @since 3.2.0
39
			 *
40
			 * @param string sites.
41
			 */
42
			do_action( 'wpcom_json_api_objects', 'sites' );
43
			return $this->get_settings_response();
44
		} else if ( 'POST' === $this->api->method ) {
45
			return $this->update_settings();
46
		} else {
47
			return new WP_Error( 'bad_request', 'An unsupported request method was used.' );
48
		}
49
50
	}
51
52
	/**
53
	 * Determines whether jetpack_relatedposts is supported
54
	 *
55
	 * @return (bool)
56
	 */
57
	public function jetpack_relatedposts_supported() {
58
		$wpcom_related_posts_theme_blacklist = array(
59
			'Expound',
60
			'Traveler',
61
			'Opti',
62
			'Currents',
63
		);
64
		return ( ! in_array( wp_get_theme()->get( 'Name' ), $wpcom_related_posts_theme_blacklist ) );
65
	}
66
67
	/**
68
	 * Returns category details
69
	 *
70
	 * @return (array)
71
	 */
72
	public function get_category_details( $category ) {
73
		return array(
74
			'value' => $category->term_id,
75
			'name' => $category->name
76
		);
77
	}
78
79
	/**
80
	 * Collects the necessary information to return for a get settings response.
81
	 *
82
	 * @return (array)
83
	 */
84
	public function get_settings_response() {
85
86
		$response_format = static::$site_format;
87
		$blog_id = (int) $this->api->get_blog_id_for_output();
88
		/** This filter is documented in class.json-api-endpoints.php */
89
		$is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
0 ignored issues
show
Unused Code introduced by
$is_jetpack is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
90
91
		foreach ( array_keys( $response_format ) as $key ) {
92
93
			// refactoring to change lang parameter to locale in 1.2
94
			if ( $lang_or_locale = $this->get_locale( $key ) ) {
95
				$response[$key] = $lang_or_locale;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$response was never initialized. Although not strictly required by PHP, it is generally a good practice to add $response = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
96
				continue;
97
			}
98
99
			switch ( $key ) {
100
			case 'ID' :
101
				$response[$key] = $blog_id;
0 ignored issues
show
Bug introduced by
The variable $response does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
102
				break;
103
			case 'name' :
104
				$response[$key] = (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
105
				break;
106
			case 'description' :
107
				$response[$key] = (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES );
108
				break;
109
			case 'URL' :
110
				$response[$key] = (string) home_url();
111
				break;
112
			case 'settings':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
113
114
				$jetpack_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' );
115
116
				if ( method_exists( 'Jetpack', 'is_module_active' ) ) {
117
					$jetpack_relatedposts_options[ 'enabled' ] = Jetpack::is_module_active( 'related-posts' );
118
				}
119
120
				// array_values() is necessary to ensure the array starts at index 0.
121
				$post_categories = array_values(
122
					array_map(
123
						array( $this, 'get_category_details' ),
124
						get_categories( array( 'hide_empty' => false ) )
125
					)
126
				);
127
128
				$eventbrite_api_token = (int) get_option( 'eventbrite_api_token' );
129
				if ( 0 === $eventbrite_api_token ) {
130
					$eventbrite_api_token = null;
131
				}
132
133
				$holiday_snow = false;
134
				if ( function_exists( 'jetpack_holiday_snow_option_name' ) ) {
135
					$holiday_snow = (bool) get_option( jetpack_holiday_snow_option_name() );
136
				}
137
138
				$response[ $key ] = array(
139
140
					// also exists as "options"
141
					'admin_url'               => get_admin_url(),
142
					'default_ping_status'     => (bool) ( 'closed' != get_option( 'default_ping_status' ) ),
143
					'default_comment_status'  => (bool) ( 'closed' != get_option( 'default_comment_status' ) ),
144
145
					// new stuff starts here
146
					'blog_public'             => (int) get_option( 'blog_public' ),
147
					'jetpack_sync_non_public_post_stati' => (bool) Jetpack_Options::get_option( 'sync_non_public_post_stati' ),
148
					'jetpack_relatedposts_allowed' => (bool) $this->jetpack_relatedposts_supported(),
149
					'jetpack_relatedposts_enabled' => (bool) $jetpack_relatedposts_options[ 'enabled' ],
150
					'jetpack_relatedposts_show_headline' => (bool) $jetpack_relatedposts_options[ 'show_headline' ],
151
					'jetpack_relatedposts_show_thumbnails' => (bool) $jetpack_relatedposts_options[ 'show_thumbnails' ],
152
					'default_category'        => (int) get_option('default_category'),
153
					'post_categories'         => (array) $post_categories,
154
					'default_post_format'     => get_option( 'default_post_format' ),
155
					'default_pingback_flag'   => (bool) get_option( 'default_pingback_flag' ),
156
					'require_name_email'      => (bool) get_option( 'require_name_email' ),
157
					'comment_registration'    => (bool) get_option( 'comment_registration' ),
158
					'close_comments_for_old_posts' => (bool) get_option( 'close_comments_for_old_posts' ),
159
					'close_comments_days_old' => (int) get_option( 'close_comments_days_old' ),
160
					'thread_comments'         => (bool) get_option( 'thread_comments' ),
161
					'thread_comments_depth'   => (int) get_option( 'thread_comments_depth' ),
162
					'page_comments'           => (bool) get_option( 'page_comments' ),
163
					'comments_per_page'       => (int) get_option( 'comments_per_page' ),
164
					'default_comments_page'   => get_option( 'default_comments_page' ),
165
					'comment_order'           => get_option( 'comment_order' ),
166
					'comments_notify'         => (bool) get_option( 'comments_notify' ),
167
					'moderation_notify'       => (bool) get_option( 'moderation_notify' ),
168
					'social_notifications_like' => ( "on" == get_option( 'social_notifications_like' ) ),
169
					'social_notifications_reblog' => ( "on" == get_option( 'social_notifications_reblog' ) ),
170
					'social_notifications_subscribe' => ( "on" == get_option( 'social_notifications_subscribe' ) ),
171
					'comment_moderation'      => (bool) get_option( 'comment_moderation' ),
172
					'comment_whitelist'       => (bool) get_option( 'comment_whitelist' ),
173
					'comment_max_links'       => (int) get_option( 'comment_max_links' ),
174
					'moderation_keys'         => get_option( 'moderation_keys' ),
175
					'blacklist_keys'          => get_option( 'blacklist_keys' ),
176
					'lang_id'                 => get_option( 'lang_id' ),
177
					'wga'                     => get_option( 'wga' ),
178
					'disabled_likes'          => (bool) get_option( 'disabled_likes' ),
179
					'disabled_reblogs'        => (bool) get_option( 'disabled_reblogs' ),
180
					'jetpack_comment_likes_enabled' => (bool) get_option( 'jetpack_comment_likes_enabled', false ),
181
					'twitter_via'             => (string) get_option( 'twitter_via' ),
182
					'jetpack-twitter-cards-site-tag' => (string) get_option( 'jetpack-twitter-cards-site-tag' ),
183
					'eventbrite_api_token'    => $eventbrite_api_token,
184
					'holidaysnow'             => $holiday_snow
185
				);
186
187
				//allow future versions of this endpoint to support additional settings keys
188
				/**
189
				 * Filter the current site setting in the returned response.
190
				 *
191
				 * @module json-api
192
				 *
193
				 * @since 3.9.3
194
				 *
195
				 * @param mixed $response_item A single site setting.
196
				 */
197
				$response[ $key ] = apply_filters( 'site_settings_endpoint_get', $response[ $key ] );
198
199
				if ( class_exists( 'Sharing_Service' ) ) {
200
					$ss = new Sharing_Service();
201
					$sharing = $ss->get_global_options();
202
					$response[ $key ]['sharing_button_style'] = (string) $sharing['button_style'];
203
					$response[ $key ]['sharing_label'] = (string) $sharing['sharing_label'];
204
					$response[ $key ]['sharing_show'] = (array) $sharing['show'];
205
					$response[ $key ]['sharing_open_links'] = (string) $sharing['open_links'];
206
				}
207
208
				if ( function_exists( 'jetpack_protect_format_whitelist' ) ) {
209
					$response[ $key ]['jetpack_protect_whitelist'] = jetpack_protect_format_whitelist();
210
				}
211
212
				if ( ! current_user_can( 'edit_posts' ) )
213
					unset( $response[$key] );
214
				break;
215
			}
216
		}
217
218
		return $response;
219
220
	}
221
222 View Code Duplication
	protected function get_locale( $key ) {
223
		if ( 'lang' == $key ) {
224
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
225
				return (string) get_blog_lang_code();
226
			} else {
227
				return get_locale();
228
			}
229
		}
230
231
		return false;
232
	}
233
234
235
	/**
236
	 * Updates site settings for authorized users
237
	 *
238
	 * @return (array)
239
	 */
240
	public function update_settings() {
241
242
		// $this->input() retrieves posted arguments whitelisted and casted to the $request_format
243
		// specs that get passed in when this class is instantiated
244
		/**
245
		 * Filters the settings to be updated on the site.
246
		 *
247
		 * @module json-api
248
		 *
249
		 * @since 3.6.0
250
		 *
251
		 * @param array $input Associative array of site settings to be updated.
252
		 */
253
		$input = apply_filters( 'rest_api_update_site_settings', $this->input() );
254
255
		$jetpack_relatedposts_options = array();
256
		$sharing_options = array();
257
		$updated = array();
258
259
		foreach ( $input as $key => $value ) {
260
261
			if ( ! is_array( $value ) ) {
262
				$value = trim( $value );
263
			}
264
			$value = wp_unslash( $value );
265
266
			switch ( $key ) {
267
268
				case 'default_ping_status':
269
				case 'default_comment_status':
270
					// settings are stored as closed|open
271
					$coerce_value = ( $value ) ? 'open' : 'closed';
272
					if ( update_option( $key, $coerce_value ) ) {
273
						$updated[ $key ] = $value;
274
					};
275
					break;
276
				case 'jetpack_protect_whitelist':
277
					if ( function_exists( 'jetpack_protect_save_whitelist' ) ) {
278
						$result = jetpack_protect_save_whitelist( $value );
279
						if ( is_wp_error( $result ) ) {
280
							return $result;
281
						}
282
						$updated[ $key ] = jetpack_protect_format_whitelist();
283
					}
284
					break;
285
				case 'jetpack_sync_non_public_post_stati':
286
					Jetpack_Options::update_option( 'sync_non_public_post_stati', $value );
287
					break;
288
				case 'jetpack_relatedposts_enabled':
289
				case 'jetpack_relatedposts_show_thumbnails':
290
				case 'jetpack_relatedposts_show_headline':
291
					if ( ! $this->jetpack_relatedposts_supported() ) {
292
						break;
293
					}
294
					if ( 'jetpack_relatedposts_enabled' === $key && method_exists( 'Jetpack', 'is_module_active' ) && $this->jetpack_relatedposts_supported() ) {
295
						$before_action = Jetpack::is_module_active('related-posts');
296
						if ( $value ) {
297
							Jetpack::activate_module( 'related-posts', false, false );
298
						} else {
299
							Jetpack::deactivate_module( 'related-posts' );
300
						}
301
						$after_action = Jetpack::is_module_active('related-posts');
302
						if ( $after_action == $before_action ) {
303
							break;
304
						}
305
					}
306
					$just_the_key = substr( $key, 21 );
307
					$jetpack_relatedposts_options[ $just_the_key ] = $value;
308
				break;
309
310
				case 'social_notifications_like':
311
				case 'social_notifications_reblog':
312
				case 'social_notifications_subscribe':
313
					// settings are stored as on|off
314
					$coerce_value = ( $value ) ? 'on' : 'off';
315
					if ( update_option( $key, $coerce_value ) ) {
316
						$updated[ $key ] = $value;
317
					}
318
					break;
319
				case 'wga':
320
					if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^UA-[\d-]+$/i', $value['code'] ) ) {
321
						return new WP_Error( 'invalid_code', 'Invalid UA ID' );
322
					}
323
					$wga = get_option( 'wga', array() );
324
					$wga['code'] = $value['code']; // maintain compatibility with wp-google-analytics
325
					if ( update_option( 'wga', $wga ) ) {
326
						$updated[ $key ] = $value;
327
					}
328
329
					$enabled_or_disabled = $wga['code'] ? 'enabled' : 'disabled';
330
331
					/** This action is documented in modules/widgets/social-media-icons.php */
332
					do_action( 'jetpack_bump_stats_extras', 'google-analytics', $enabled_or_disabled );
333
334
					$business_plugins = WPCOM_Business_Plugins::instance();
335
					$business_plugins->activate_plugin( 'wp-google-analytics' );
336
					break;
337
338
				case 'jetpack_comment_likes_enabled':
339
					// settings are stored as 1|0
340
					$coerce_value = (int) $value;
341
					if ( update_option( $key, $coerce_value ) ) {
342
						$updated[ $key ] = $value;
343
					}
344
					break;
345
346
				// Sharing options
347
				case 'sharing_button_style':
348
				case 'sharing_show':
349
				case 'sharing_open_links':
350
					$sharing_options[ preg_replace( '/^sharing_/', '', $key ) ] = $value;
351
					break;
352
				case 'sharing_label':
353
					$sharing_options[ $key ] = $value;
354
					break;
355
356
				// Keyring token option
357
				case 'eventbrite_api_token':
358
					// These options can only be updated for sites hosted on WordPress.com
359
					if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
360
						if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
361
							if ( delete_option( $key ) ) {
362
								$updated[ $key ] = null;
363
							}
364
						} else if ( update_option( $key, $value ) ) {
365
							$updated[ $key ] = (int) $value;
366
						}
367
					}
368
					break;
369
370
				case 'holidaysnow':
371
					if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
372
						if ( function_exists( 'jetpack_holiday_snow_option_name' ) && delete_option( jetpack_holiday_snow_option_name() ) ) {
373
							$updated[ $key ] = false;
374
						}
375
					} else if ( function_exists( 'jetpack_holiday_snow_option_name' ) && update_option( jetpack_holiday_snow_option_name(), 'letitsnow' ) ) {
376
						$updated[ $key ] = true;
377
					}
378
					break;
379
380
381
				default:
382
					//allow future versions of this endpoint to support additional settings keys
383
					if ( has_filter( 'site_settings_endpoint_update_' . $key ) ) {
384
						/**
385
						 * Filter current site setting value to be updated.
386
						 *
387
						 * @module json-api
388
						 *
389
						 * @since 3.9.3
390
						 *
391
						 * @param mixed $response_item A single site setting value.
392
						 */
393
						$value = apply_filters( 'site_settings_endpoint_update_' . $key, $value );
394
						$updated[ $key ] = $value;
395
						continue;
396
					}
397
398
					// no worries, we've already whitelisted and casted arguments above
399
					if ( update_option( $key, $value ) ) {
400
						$updated[ $key ] = $value;
401
					}
402
403
			}
404
		}
405
406
		if ( count( $jetpack_relatedposts_options ) ) {
407
			// track new jetpack_relatedposts options against old
408
			$old_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' );
409
			if ( Jetpack_Options::update_option( 'relatedposts', $jetpack_relatedposts_options ) ) {
410
				foreach( $jetpack_relatedposts_options as $key => $value ) {
411
					if ( $value !== $old_relatedposts_options[ $key ] ) {
412
						$updated[ 'jetpack_relatedposts_' . $key ] = $value;
413
					}
414
				}
415
			}
416
		}
417
418
		if ( ! empty( $sharing_options ) && class_exists( 'Sharing_Service' ) ) {
419
			$ss = new Sharing_Service();
420
421
			// Merge current values with updated, since Sharing_Service expects
422
			// all values to be included when updating
423
			$current_sharing_options = $ss->get_global_options();
424
			foreach ( $current_sharing_options as $key => $val ) {
425
				if ( ! isset( $sharing_options[ $key ] ) ) {
426
					$sharing_options[ $key ] = $val;
427
				}
428
			}
429
430
			$updated_social_options = $ss->set_global_options( $sharing_options );
431
432
			if ( isset( $input['sharing_button_style'] ) ) {
433
				$updated['sharing_button_style'] = (string) $updated_social_options['button_style'];
434
			}
435
			if ( isset( $input['sharing_label'] ) ) {
436
				// Sharing_Service won't report label as updated if set to default
437
				$updated['sharing_label'] = (string) $sharing_options['sharing_label'];
438
			}
439
			if ( isset( $input['sharing_show'] ) ) {
440
				$updated['sharing_show'] = (array) $updated_social_options['show'];
441
			}
442
			if ( isset( $input['sharing_open_links'] ) ) {
443
				$updated['sharing_open_links'] = (string) $updated_social_options['open_links'];
444
			}
445
		}
446
447
		return array(
448
			'updated' => $updated
449
		);
450
451
	}
452
}
453