Completed
Push — merge/json-api-site-settings-f... ( d4d0fa )
by
unknown
10:51
created

get_locale()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 9.2
cc 4
eloc 7
nc 3
nop 1
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
				$response[ $key ] = apply_filters( 'site_settings_endpoint_get', $response[ $key ] );
189
190
				if ( class_exists( 'Sharing_Service' ) ) {
191
					$ss = new Sharing_Service();
192
					$sharing = $ss->get_global_options();
193
					$response[ $key ]['sharing_button_style'] = (string) $sharing['button_style'];
194
					$response[ $key ]['sharing_label'] = (string) $sharing['sharing_label'];
195
					$response[ $key ]['sharing_show'] = (array) $sharing['show'];
196
					$response[ $key ]['sharing_open_links'] = (string) $sharing['open_links'];
197
				}
198
199
				if ( function_exists( 'jetpack_protect_format_whitelist' ) ) {
200
					$response[ $key ]['jetpack_protect_whitelist'] = jetpack_protect_format_whitelist();
201
				}
202
203
				if ( ! current_user_can( 'edit_posts' ) )
204
					unset( $response[$key] );
205
				break;
206
			}
207
		}
208
209
		return $response;
210
211
	}
212
213
	protected function get_locale( $key ) {
214
		if ( 'lang' == $key ) {
215
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
216
				return (string) get_blog_lang_code();
217
			} else {
218
				return get_locale();
219
			}
220
		}
221
222
		return false;
223
	}
224
225
226
	/**
227
	 * Updates site settings for authorized users
228
	 *
229
	 * @return (array)
230
	 */
231
	public function update_settings() {
232
233
		// $this->input() retrieves posted arguments whitelisted and casted to the $request_format
234
		// specs that get passed in when this class is instantiated
235
		/**
236
		 * Filters the settings to be updated on the site.
237
		 *
238
		 * @module json-api
239
		 *
240
		 * @since 3.6.0
241
		 *
242
		 * @param array $input Associative array of site settings to be updated.
243
		 */
244
		$input = apply_filters( 'rest_api_update_site_settings', $this->input() );
245
246
		$jetpack_relatedposts_options = array();
247
		$sharing_options = array();
248
		$updated = array();
249
250
		foreach ( $input as $key => $value ) {
251
252
			if ( ! is_array( $value ) ) {
253
				$value = trim( $value );
254
			}
255
			$value = wp_unslash( $value );
256
257
			switch ( $key ) {
258
259
				case 'default_ping_status':
260
				case 'default_comment_status':
261
					// settings are stored as closed|open
262
					$coerce_value = ( $value ) ? 'open' : 'closed';
263
					if ( update_option( $key, $coerce_value ) ) {
264
						$updated[ $key ] = $value;
265
					};
266
					break;
267
				case 'jetpack_protect_whitelist':
268
					if ( function_exists( 'jetpack_protect_save_whitelist' ) ) {
269
						$result = jetpack_protect_save_whitelist( $value );
270
						if ( is_wp_error( $result ) ) {
271
							return $result;
272
						}
273
						$updated[ $key ] = jetpack_protect_format_whitelist();
274
					}
275
					break;
276
				case 'jetpack_sync_non_public_post_stati':
277
					Jetpack_Options::update_option( 'sync_non_public_post_stati', $value );
278
					break;
279
				case 'jetpack_relatedposts_enabled':
280
				case 'jetpack_relatedposts_show_thumbnails':
281
				case 'jetpack_relatedposts_show_headline':
282
					if ( ! $this->jetpack_relatedposts_supported() ) {
283
						break;
284
					}
285
					if ( 'jetpack_relatedposts_enabled' === $key && method_exists( 'Jetpack', 'is_module_active' ) && $this->jetpack_relatedposts_supported() ) {
286
						$before_action = Jetpack::is_module_active('related-posts');
287
						if ( $value ) {
288
							Jetpack::activate_module( 'related-posts', false, false );
289
						} else {
290
							Jetpack::deactivate_module( 'related-posts' );
291
						}
292
						$after_action = Jetpack::is_module_active('related-posts');
293
						if ( $after_action == $before_action ) {
294
							break;
295
						}
296
					}
297
					$just_the_key = substr( $key, 21 );
298
					$jetpack_relatedposts_options[ $just_the_key ] = $value;
299
				break;
300
301
				case 'social_notifications_like':
302
				case 'social_notifications_reblog':
303
				case 'social_notifications_subscribe':
304
					// settings are stored as on|off
305
					$coerce_value = ( $value ) ? 'on' : 'off';
306
					if ( update_option( $key, $coerce_value ) ) {
307
						$updated[ $key ] = $value;
308
					}
309
					break;
310
				case 'wga':
311
					if ( ! isset( $value['code'] ) || ! preg_match( '/^$|^UA-[\d-]+$/i', $value['code'] ) ) {
312
						return new WP_Error( 'invalid_code', 'Invalid UA ID' );
313
					}
314
					$wga = get_option( 'wga', array() );
315
					$wga['code'] = $value['code']; // maintain compatibility with wp-google-analytics
316
					if ( update_option( 'wga', $wga ) ) {
317
						$updated[ $key ] = $value;
318
					}
319
320
					$enabled_or_disabled = $wga['code'] ? 'enabled' : 'disabled';
321
322
					/** This action is documented in modules/widgets/social-media-icons.php */
323
					do_action( 'jetpack_bump_stats_extras', 'google-analytics', $enabled_or_disabled );
324
325
					$business_plugins = WPCOM_Business_Plugins::instance();
326
					$business_plugins->activate_plugin( 'wp-google-analytics' );
327
					break;
328
329
				case 'jetpack_comment_likes_enabled':
330
					// settings are stored as 1|0
331
					$coerce_value = (int) $value;
332
					if ( update_option( $key, $coerce_value ) ) {
333
						$updated[ $key ] = $value;
334
					}
335
					break;
336
337
				// Sharing options
338
				case 'sharing_button_style':
339
				case 'sharing_show':
340
				case 'sharing_open_links':
341
					$sharing_options[ preg_replace( '/^sharing_/', '', $key ) ] = $value;
342
					break;
343
				case 'sharing_label':
344
					$sharing_options[ $key ] = $value;
345
					break;
346
347
				// Keyring token option
348
				case 'eventbrite_api_token':
349
					// These options can only be updated for sites hosted on WordPress.com
350
					if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
351
						if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
352
							if ( delete_option( $key ) ) {
353
								$updated[ $key ] = null;
354
							}
355
						} else if ( update_option( $key, $value ) ) {
356
							$updated[ $key ] = (int) $value;
357
						}
358
					}
359
					break;
360
361
				case 'holidaysnow':
362
					if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
363
						if ( function_exists( 'jetpack_holiday_snow_option_name' ) && delete_option( jetpack_holiday_snow_option_name() ) ) {
364
							$updated[ $key ] = false;
365
						}
366
					} else if ( function_exists( 'jetpack_holiday_snow_option_name' ) && update_option( jetpack_holiday_snow_option_name(), 'letitsnow' ) ) {
367
						$updated[ $key ] = true;
368
					}
369
					break;
370
371
372
				default:
373
					//allow future versions of this endpoint to support additional settings keys
374
					if ( has_filter( 'site_settings_endpoint_update_' . $key ) ) {
375
						$value = apply_filters( 'site_settings_endpoint_update_' . $key, $value );
376
						$updated[ $key ] = $value;
377
						continue;
378
					}
379
380
					// no worries, we've already whitelisted and casted arguments above
381
					if ( update_option( $key, $value ) ) {
382
						$updated[ $key ] = $value;
383
					}
384
385
			}
386
		}
387
388
		if ( count( $jetpack_relatedposts_options ) ) {
389
			// track new jetpack_relatedposts options against old
390
			$old_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' );
391
			if ( Jetpack_Options::update_option( 'relatedposts', $jetpack_relatedposts_options ) ) {
392
				foreach( $jetpack_relatedposts_options as $key => $value ) {
393
					if ( $value !== $old_relatedposts_options[ $key ] ) {
394
						$updated[ 'jetpack_relatedposts_' . $key ] = $value;
395
					}
396
				}
397
			}
398
		}
399
400
		if ( ! empty( $sharing_options ) && class_exists( 'Sharing_Service' ) ) {
401
			$ss = new Sharing_Service();
402
403
			// Merge current values with updated, since Sharing_Service expects
404
			// all values to be included when updating
405
			$current_sharing_options = $ss->get_global_options();
406
			foreach ( $current_sharing_options as $key => $val ) {
407
				if ( ! isset( $sharing_options[ $key ] ) ) {
408
					$sharing_options[ $key ] = $val;
409
				}
410
			}
411
412
			$updated_social_options = $ss->set_global_options( $sharing_options );
413
414
			if ( isset( $input['sharing_button_style'] ) ) {
415
				$updated['sharing_button_style'] = (string) $updated_social_options['button_style'];
416
			}
417
			if ( isset( $input['sharing_label'] ) ) {
418
				// Sharing_Service won't report label as updated if set to default
419
				$updated['sharing_label'] = (string) $sharing_options['sharing_label'];
420
			}
421
			if ( isset( $input['sharing_show'] ) ) {
422
				$updated['sharing_show'] = (array) $updated_social_options['show'];
423
			}
424
			if ( isset( $input['sharing_open_links'] ) ) {
425
				$updated['sharing_open_links'] = (string) $updated_social_options['open_links'];
426
			}
427
		}
428
429
		return array(
430
			'updated' => $updated
431
		);
432
433
	}
434
}
435