Completed
Push — update/crm-integration-fixes ( a3fc2b )
by Jeremy
14:51 queued 06:26
created

WPCOM_JSON_API_GET_Site_Endpoint   F

Complexity

Total Complexity 138

Size/Duplication

Total Lines 679
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 138
lcom 1
cbo 5
dl 0
loc 679
rs 1.921
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A callback() 0 23 5
A filter_fields_and_options() 0 6 3
B build_current_site_response() 0 34 6
A has_user_access() 0 3 1
A has_blog_access() 0 10 4
A render_response_keys() 0 15 2
F render_response_key() 0 123 39
F render_option_keys() 0 215 68
A build_meta_response() 0 18 3
B decorate_jetpack_response() 0 43 7

How to fix   Complexity   

Complex Class

Complex classes like WPCOM_JSON_API_GET_Site_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_GET_Site_Endpoint, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
new WPCOM_JSON_API_GET_Site_Endpoint( array(
4
	'description' => 'Get information about a site.',
5
	'group'       => 'sites',
6
	'stat'        => 'sites:X',
7
	'allowed_if_flagged' => true,
8
	'method'      => 'GET',
9
	'max_version' => '1.1',
10
	'new_version' => '1.2',
11
	'path'        => '/sites/%s',
12
	'path_labels' => array(
13
		'$site' => '(int|string) Site ID or domain',
14
	),
15
	'allow_jetpack_site_auth' => true,
16
	'query_parameters' => array(
17
		'context' => false,
18
		'options' => '(string) Optional. Returns specified options only. Comma-separated list. Example: options=login_url,timezone',
19
	),
20
21
	'response_format' => WPCOM_JSON_API_GET_Site_Endpoint::$site_format,
22
23
	'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/',
24
) );
25
26
class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
27
28
	public static $site_format = array(
29
		'ID'                          => '(int) Site ID',
30
		'name'                        => '(string) Title of site',
31
		'description'                 => '(string) Tagline or description of site',
32
		'URL'                         => '(string) Full URL to the site',
33
		'user_can_manage'             => '(bool) The current user can manage this site', // deprecated.
34
		'capabilities'                => '(array) Array of capabilities for the current user on this site.',
35
		'jetpack'                     => '(bool) Whether the site is a Jetpack site or not',
36
		'jetpack_connection'          => '(bool) Whether the site is connected to WP.com via `jetpack-connection`',
37
		'is_multisite'                => '(bool) Whether the site is a Multisite site or not. Always true for WP.com sites.',
38
		'post_count'                  => '(int) The number of posts the site has',
39
		'subscribers_count'           => '(int) The number of subscribers the site has',
40
		'lang'                        => '(string) Primary language code of the site',
41
		'icon'                        => '(array) An array of icon formats for the site',
42
		'logo'                        => '(array) The site logo, set in the Customizer',
43
		'visible'                     => '(bool) If this site is visible in the user\'s site list',
44
		'is_private'                  => '(bool) If the site is a private site or not',
45
		'is_coming_soon'              => '(bool) If the site is marked as "coming soon" or not',
46
		'single_user_site'            => '(bool) Whether the site is single user. Only returned for WP.com sites and for Jetpack sites with version 3.4 or higher.',
47
		'is_vip'                      => '(bool) If the site is a VIP site or not.',
48
		'is_following'                => '(bool) If the current user is subscribed to this site in the reader',
49
		'options'                     => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site. Note: Post formats is deprecated, please see /sites/$id/post-formats/',
50
		'plan'                        => '(array) Details of the current plan for this site.',
51
		'updates'                     => '(array) An array of available updates for plugins, themes, wordpress, and languages.',
52
		'jetpack_modules'             => '(array) A list of active Jetpack modules.',
53
		'meta'                        => '(object) Meta data',
54
		'quota'                       => '(array) An array describing how much space a user has left for uploads',
55
		'launch_status'               => '(string) A string describing the launch status of a site',
56
		'site_migration'              => '(array) Data about any migration into the site.',
57
		'is_fse_active'               => '(bool) If the site has Full Site Editing active or not.',
58
		'is_fse_eligible'             => '(bool) If the site is capable of Full Site Editing or not',
59
		'is_core_site_editor_enabled' => '(bool) If the site has the core site editor enabled.',
60
	);
61
62
	protected static $no_member_fields = array(
63
		'ID',
64
		'name',
65
		'description',
66
		'URL',
67
		'jetpack',
68
		'jetpack_connection',
69
		'post_count',
70
		'subscribers_count',
71
		'lang',
72
		'locale',
73
		'icon',
74
		'logo',
75
		'visible',
76
		'is_private',
77
		'is_coming_soon',
78
		'is_following',
79
		'meta',
80
		'launch_status',
81
		'site_migration',
82
		'is_fse_active',
83
		'is_fse_eligible',
84
		'is_core_site_editor_enabled',
85
	);
86
87
	protected static $site_options_format = array(
88
		'timezone',
89
		'gmt_offset',
90
		'blog_public',
91
		'videopress_enabled',
92
		'upgraded_filetypes_enabled',
93
		'login_url',
94
		'admin_url',
95
		'is_mapped_domain',
96
		'is_redirect',
97
		'unmapped_url',
98
		'featured_images_enabled',
99
		'theme_slug',
100
		'header_image',
101
		'background_color',
102
		'image_default_link_type',
103
		'image_thumbnail_width',
104
		'image_thumbnail_height',
105
		'image_thumbnail_crop',
106
		'image_medium_width',
107
		'image_medium_height',
108
		'image_large_width',
109
		'image_large_height',
110
		'permalink_structure',
111
		'post_formats',
112
		'default_post_format',
113
		'default_category',
114
		'allowed_file_types',
115
		'show_on_front',
116
		/** This filter is documented in modules/likes.php */
117
		'default_likes_enabled',
118
		'default_sharing_status',
119
		'default_comment_status',
120
		'default_ping_status',
121
		'software_version',
122
		'created_at',
123
		'wordads',
124
		'publicize_permanently_disabled',
125
		'frame_nonce',
126
		'jetpack_frame_nonce',
127
		'page_on_front',
128
		'page_for_posts',
129
		'headstart',
130
		'headstart_is_fresh',
131
		'ak_vp_bundle_enabled',
132
		Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION,
133
		Jetpack_SEO_Titles::TITLE_FORMATS_OPTION,
134
		'verification_services_codes',
135
		'podcasting_archive',
136
		'is_domain_only',
137
		'is_automated_transfer',
138
		'is_wpcom_atomic',
139
		'is_wpcom_store',
140
		'signup_is_store',
141
		'has_pending_automated_transfer',
142
		'woocommerce_is_active',
143
		'design_type',
144
		'site_goals',
145
		'site_segment',
146
		'import_engine',
147
		'is_wpforteams_site',
148
		'site_creation_flow',
149
		'is_cloud_eligible',
150
	);
151
152
	protected static $jetpack_response_field_additions = array(
153
		'subscribers_count',
154
		'site_migration',
155
	);
156
157
	protected static $jetpack_response_field_member_additions = array(
158
		'capabilities',
159
		'plan',
160
		'products',
161
	);
162
163
	protected static $jetpack_response_option_additions = array(
164
		'publicize_permanently_disabled',
165
		'ak_vp_bundle_enabled',
166
		'is_automated_transfer',
167
		'is_wpcom_atomic',
168
		'is_wpcom_store',
169
		'woocommerce_is_active',
170
		'frame_nonce',
171
		'jetpack_frame_nonce',
172
		'design_type',
173
		'wordads',
174
		// Use the site registered date from wpcom, since it is only available in a multisite context
175
		// and defaults to `0000-00-00T00:00:00+00:00` from the Jetpack site.
176
		// See https://github.com/Automattic/jetpack/blob/58638f46094b36f5df9cbc4570006544f0ad300c/sal/class.json-api-site-base.php#L387.
177
		'created_at',
178
	);
179
180
	private $site;
181
182
	// protected $compact = null;
183
	protected $fields_to_include = '_all';
184
	protected $options_to_include = '_all';
185
186
	// /sites/mine
187
	// /sites/%s -> $blog_id
188
	function callback( $path = '', $blog_id = 0 ) {
189
		if ( 'mine' === $blog_id ) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of 'mine' (string) and $blog_id (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
190
			$api = WPCOM_JSON_API::init();
191
			if ( ! $api->token_details || empty( $api->token_details['blog_id'] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $api->token_details of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
192
				return new WP_Error( 'authorization_required', 'An active access token must be used to query information about the current blog.', 403 );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'authorization_required'.

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...
193
			}
194
			$blog_id = $api->token_details['blog_id'];
195
		}
196
197
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
198
		if ( is_wp_error( $blog_id ) ) {
199
			return $blog_id;
200
		}
201
202
		$this->filter_fields_and_options();
203
204
		$response = $this->build_current_site_response();
205
206
		/** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
207
		do_action( 'wpcom_json_api_objects', 'sites' );
208
209
		return $response;
210
	}
211
212
	public function filter_fields_and_options() {
213
		$query_args = $this->query_args();
214
215
		$this->fields_to_include  = empty( $query_args['fields'] ) ? '_all' : array_map( 'trim', explode( ',', $query_args['fields'] ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like empty($query_args['field...$query_args['fields'])) can also be of type array. However, the property $fields_to_include is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
216
		$this->options_to_include = empty( $query_args['options'] ) ? '_all' : array_map( 'trim', explode( ',', $query_args['options'] ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like empty($query_args['optio...query_args['options'])) can also be of type array. However, the property $options_to_include is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
217
	}
218
219
	/**
220
	 * Collects the necessary information to return for a site's response.
221
	 *
222
	 * @return array
223
	 */
224
	public function build_current_site_response() {
225
226
		$blog_id = (int) $this->api->get_blog_id_for_output();
227
228
		$this->site = $this->get_platform()->get_site( $blog_id );
229
230
		/**
231
 		 * Filter the structure of information about the site to return.
232
 		 *
233
 		 * @module json-api
234
 		 *
235
 		 * @since 3.9.3
236
 		 *
237
 		 * @param array $site_format Data structure.
238
 		 */
239
		$default_fields = array_keys( apply_filters( 'sites_site_format', self::$site_format ) );
240
241
		$response_keys = is_array( $this->fields_to_include ) ?
242
			array_intersect( $default_fields, $this->fields_to_include ) :
243
			$default_fields;
244
245
		$has_blog_access = $this->has_blog_access( $this->api->token_details );
246
		$has_user_access = $this->has_user_access();
247
248
		if ( ! $has_user_access && ! $has_blog_access ) {
249
			// Public access without user or blog auth, only return `$no_member_fields`.
250
			$response_keys = array_intersect( $response_keys, self::$no_member_fields );
251
		} elseif ( $has_user_access && ! current_user_can( 'edit_posts' ) ) {
252
			// Subscriber level user, don't return site options.
253
			$response_keys = array_diff( $response_keys, array( 'options' ) );
254
		}
255
256
		return $this->render_response_keys( $response_keys );
257
	}
258
259
	/**
260
	 * Checks that the current user has access to the current blog.
261
	 *
262
	 * @return bool Whether or not the current user can access the current blog.
263
	 */
264
	private function has_user_access() {
265
		return is_user_member_of_blog( get_current_user_id(), get_current_blog_id() );
266
	}
267
268
	/**
269
	 * Checks if the request has a valid blog token for the current blog.
270
	 *
271
	 * @param array $token_details Access token for the api request.
272
	 * @return bool
273
	 */
274
	private function has_blog_access( $token_details ) {
275
		$token_details = (array) $token_details;
276
		if ( ! isset( $token_details['access'], $token_details['auth'], $token_details['blog_id'] ) ) {
277
			return false;
278
		}
279
280
		return 'jetpack' === $token_details['auth'] &&
281
			'blog' === $token_details['access'] &&
282
			get_current_blog_id() === $token_details['blog_id'];
283
	}
284
285
	private function render_response_keys( &$response_keys ) {
286
		$response = array();
287
288
		$is_user_logged_in = is_user_logged_in();
289
290
		$this->site->before_render();
291
292
		foreach ( $response_keys as $key ) {
293
			$this->render_response_key( $key, $response, $is_user_logged_in );
294
		}
295
296
		$this->site->after_render( $response );
297
298
		return $response;
299
	}
300
301
	protected function render_response_key( $key, &$response, $is_user_logged_in ) {
302
		do_action( 'pre_render_site_response_key', $key );
303
304
		switch ( $key ) {
305
			case 'ID' :
306
				$response[ $key ] = $this->site->blog_id;
307
				break;
308
			case 'name' :
309
				$response[ $key ] = $this->site->get_name();
310
				break;
311
			case 'description' :
312
				$response[ $key ] = $this->site->get_description();
313
				break;
314
			case 'URL' :
315
				$response[ $key ] = $this->site->get_url();
316
				break;
317
			case 'user_can_manage' :
318
				$response[ $key ] = $this->site->user_can_manage();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $response[$key] is correct as $this->site->user_can_manage() (which targets SAL_Site::user_can_manage()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
319
			case 'is_private' :
320
				$response[ $key ] = $this->site->is_private();
321
				break;
322
			case 'is_coming_soon' :
323
				// This option is stored on wp.com for both simple and atomic sites. @see mu-plugins/private-blog.php
324
				$response[ $key ] = $this->site->is_coming_soon();;
325
				break;
326
			case 'launch_status' :
327
				$response[ $key ] = $this->site->get_launch_status();
328
				break;
329
			case 'visible' :
330
				$response[ $key ] = $this->site->is_visible();
331
				break;
332
			case 'subscribers_count' :
333
				$response[ $key ] = $this->site->get_subscribers_count();
334
				break;
335
			case 'post_count' :
336
				if ( $is_user_logged_in ) {
337
					$response[ $key ] = $this->site->get_post_count();
338
				}
339
				break;
340
			case 'icon' :
341
				$icon = $this->site->get_icon();
342
343
				if ( ! is_null( $icon ) ) {
344
					$response[ $key ] = $icon;
345
				}
346
				break;
347
			case 'logo' :
348
				$response[ $key ] = $this->site->get_logo();
349
				break;
350
			case 'is_following':
351
				$response[ $key ] = $this->site->is_following();
352
				break;
353
			case 'options':
354
				// small optimisation - don't recalculate
355
				$all_options = apply_filters( 'sites_site_options_format', self::$site_options_format );
356
357
				$options_response_keys = is_array( $this->options_to_include ) ?
358
					array_intersect( $all_options, $this->options_to_include ) :
359
					$all_options;
360
361
				$options = $this->render_option_keys( $options_response_keys );
362
363
				$this->site->after_render_options( $options );
364
365
				$response[ $key ] = (object) $options;
366
				break;
367
			case 'meta':
368
				$this->build_meta_response( $response );
369
				break;
370
			case 'lang' :
371
				$response[ $key ] = $is_user_logged_in ? $this->site->get_locale() : false;
372
				break;
373
			case 'locale' :
374
				$response[ $key ] = $is_user_logged_in ? $this->site->get_locale() : false;
375
				break;
376
			case 'jetpack':
377
				$response[ $key ] = $this->site->is_jetpack();
378
				break;
379
			case 'jetpack_connection':
380
				$response[ $key ] = $this->site->is_jetpack_connection();
381
				break;
382
			case 'single_user_site' :
383
				$response[ $key ] = $this->site->is_single_user_site();
384
				break;
385
			case 'is_vip' :
386
				$response[ $key ] = $this->site->is_vip();
387
				break;
388
			case 'is_multisite' :
389
				$response[ $key ] = $this->site->is_multisite();
390
				break;
391
			case 'capabilities' :
392
				$response[ $key ] = $this->site->get_capabilities();
393
				break;
394
			case 'jetpack_modules':
395
				if ( is_user_member_of_blog() ) {
396
					$response[ $key ] = $this->site->get_jetpack_modules();
397
				}
398
				break;
399
			case 'plan' :
400
				$response[ $key ] = $this->site->get_plan();
401
				break;
402
			case 'products' :
403
				$response[ $key ] = $this->site->get_products();
404
				break;
405
			case 'quota' :
406
				$response[ $key ] = $this->site->get_quota();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $response[$key] is correct as $this->site->get_quota() (which targets SAL_Site::get_quota()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
407
				break;
408
			case 'site_migration' :
409
				$response[ $key ] = $this->site->get_migration_meta();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $response[$key] is correct as $this->site->get_migration_meta() (which targets SAL_Site::get_migration_meta()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
410
				break;
411
			case 'is_fse_active':
412
				$response[ $key ] = $this->site->is_fse_active();
413
				break;
414
			case 'is_fse_eligible':
415
				$response[ $key ] = $this->site->is_fse_eligible();
416
				break;
417
			case 'is_core_site_editor_enabled':
418
				$response[ $key ] = $this->site->is_core_site_editor_enabled();
419
				break;
420
		}
421
422
		do_action( 'post_render_site_response_key', $key );
423
	}
424
425
	protected function render_option_keys( &$options_response_keys ) {
426
		$options = array();
427
		$site = $this->site;
428
429
		$custom_front_page = $site->is_custom_front_page();
430
431
		foreach ( $options_response_keys as $key ) {
432
			switch ( $key ) {
433
				case 'timezone' :
434
					$options[ $key ] = $site->get_timezone();
435
					break;
436
				case 'gmt_offset' :
437
					$options[ $key ] = $site->get_gmt_offset();
438
					break;
439
				case 'videopress_enabled' :
440
					$options[ $key ] = $site->has_videopress();
441
					break;
442
				case 'upgraded_filetypes_enabled' :
443
					$options[ $key ] = $site->upgraded_filetypes_enabled();
444
					break;
445
				case 'login_url' :
446
					$options[ $key ] = $site->get_login_url();
447
					break;
448
				case 'admin_url' :
449
					$options[ $key ] = $site->get_admin_url();
450
					break;
451
				case 'is_mapped_domain' :
452
					$options[ $key ] = $site->is_mapped_domain();
453
					break;
454
				case 'is_redirect' :
455
					$options[ $key ] = $site->is_redirect();
456
					break;
457
				case 'unmapped_url' :
458
					$options[ $key ] = $site->get_unmapped_url();
459
					break;
460
				case 'featured_images_enabled' :
461
					$options[ $key ] = $site->featured_images_enabled();
462
					break;
463
				case 'theme_slug' :
464
					$options[ $key ] = $site->get_theme_slug();
465
					break;
466
				case 'header_image' :
467
					$options[ $key ] = $site->get_header_image();
468
					break;
469
				case 'background_color' :
470
					$options[ $key ] = $site->get_background_color();
471
					break;
472
				case 'image_default_link_type' :
473
					$options[ $key ] = $site->get_image_default_link_type();
474
					break;
475
				case 'image_thumbnail_width' :
476
					$options[ $key ] = $site->get_image_thumbnail_width();
477
					break;
478
				case 'image_thumbnail_height' :
479
					$options[ $key ] = $site->get_image_thumbnail_height();
480
					break;
481
				case 'image_thumbnail_crop' :
482
					$options[ $key ] = $site->get_image_thumbnail_crop();
483
					break;
484
				case 'image_medium_width' :
485
					$options[ $key ] = $site->get_image_medium_width();
486
					break;
487
				case 'image_medium_height' :
488
					$options[ $key ] = $site->get_image_medium_height();
489
					break;
490
				case 'image_large_width' :
491
					$options[ $key ] = $site->get_image_large_width();
492
					break;
493
				case 'image_large_height' :
494
					$options[ $key ] = $site->get_image_large_height();
495
					break;
496
				case 'permalink_structure' :
497
					$options[ $key ] = $site->get_permalink_structure();
498
					break;
499
				case 'post_formats' :
500
					$options[ $key ] = $site->get_post_formats();
501
					break;
502
				case 'default_post_format' :
503
					$options[ $key ] = $site->get_default_post_format();
504
					break;
505
				case 'default_category' :
506
					$options[ $key ] = $site->get_default_category();
507
					break;
508
				case 'allowed_file_types' :
509
					$options[ $key ] = $site->allowed_file_types();
510
					break;
511
				case 'show_on_front' :
512
					$options[ $key ] = $site->get_show_on_front();
513
					break;
514
				/** This filter is documented in modules/likes.php */
515
				case 'default_likes_enabled' :
516
					$options[ $key ] = $site->get_default_likes_enabled();
517
					break;
518
				case 'default_sharing_status' :
519
					$options[ $key ] = $site->get_default_sharing_status();
520
					break;
521
				case 'default_comment_status' :
522
					$options[ $key ] = $site->get_default_comment_status();
523
					break;
524
				case 'default_ping_status' :
525
					$options[ $key ] = $site->default_ping_status();
526
					break;
527
				case 'software_version' :
528
					$options[ $key ] = $site->get_wordpress_version();
529
					break;
530
				case 'created_at' :
531
					$options[ $key ] = $site->get_registered_date();
532
					break;
533
				case 'wordads' :
534
					$options[ $key ] = $site->has_wordads();
535
					break;
536
				case 'publicize_permanently_disabled' :
537
					$options[ $key ] = $site->is_publicize_permanently_disabled();
538
					break;
539
				case 'frame_nonce' :
540
					$options[ $key ] = $site->get_frame_nonce();
541
					break;
542
				case 'jetpack_frame_nonce' :
543
					$options[ $key ] = $site->get_jetpack_frame_nonce();
544
					break;
545
				case 'page_on_front' :
546
					if ( $custom_front_page ) {
547
						$options[ $key ] = $site->get_page_on_front();
548
					}
549
					break;
550
				case 'page_for_posts' :
551
					if ( $custom_front_page ) {
552
						$options[ $key ] = $site->get_page_for_posts();
553
					}
554
					break;
555
				case 'headstart' :
556
					$options[ $key ] = $site->is_headstart();
557
					break;
558
				case 'headstart_is_fresh' :
559
					$options[ $key ] = $site->is_headstart_fresh();
560
					break;
561
				case 'ak_vp_bundle_enabled' :
562
					$options[ $key ] = $site->get_ak_vp_bundle_enabled();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options[$key] is correct as $site->get_ak_vp_bundle_enabled() (which targets Jetpack_Site::get_ak_vp_bundle_enabled()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
563
					break;
564
				case Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION :
565
					$options[ $key ] = $site->get_jetpack_seo_front_page_description();
566
					break;
567
				case Jetpack_SEO_Titles::TITLE_FORMATS_OPTION :
568
					$options[ $key ] = $site->get_jetpack_seo_title_formats();
569
					break;
570
				case 'verification_services_codes' :
571
					$options[ $key ] = $site->get_verification_services_codes();
572
					break;
573
				case 'podcasting_archive':
574
					$options[ $key ] = $site->get_podcasting_archive();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options[$key] is correct as $site->get_podcasting_archive() (which targets Jetpack_Site::get_podcasting_archive()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
575
					break;
576
				case 'is_domain_only':
577
					$options[ $key ] = $site->is_domain_only();
578
					break;
579
				case 'is_automated_transfer':
580
					$options[ $key ] = $site->is_automated_transfer();
581
					break;
582
				case 'blog_public':
583
					$options[ $key ] = $site->get_blog_public();
584
					break;
585
				case 'is_wpcom_atomic':
586
					$options[ $key ] = $site->is_wpcom_atomic();
587
					break;
588
				case 'is_wpcom_store':
589
					$options[ $key ] = $site->is_wpcom_store();
590
					break;
591
				case 'signup_is_store':
592
					$signup_is_store = $site->signup_is_store();
593
594
					if ( $signup_is_store ) {
595
						$options[ $key ] = $site->signup_is_store();
596
					}
597
598
					break;
599
				case 'has_pending_automated_transfer':
600
					$has_pending_automated_transfer = $site->has_pending_automated_transfer();
601
602
					if ( $has_pending_automated_transfer ) {
603
						$options[ $key ] = true;
604
					}
605
606
					break;
607
				case 'woocommerce_is_active':
608
					$options[ $key ] = $site->woocommerce_is_active();
609
					break;
610
				case 'design_type':
611
					$options[ $key ] = $site->get_design_type();
612
					break;
613
				case 'site_goals':
614
					$options[ $key ] = $site->get_site_goals();
615
					break;
616
				case 'site_segment':
617
					$options[ $key ] = $site->get_site_segment();
618
					break;
619
				case 'import_engine':
620
					$options[ $key ] = $site->get_import_engine();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $options[$key] is correct as $site->get_import_engine() (which targets Jetpack_Site::get_import_engine()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
621
					break;
622
623
				case 'is_wpforteams_site':
624
					$options[ $key ] = $site->is_wpforteams_site();
625
					break;
626
				case 'site_creation_flow':
627
					$site_creation_flow = $site->get_site_creation_flow();
628
					if ( $site_creation_flow ) {
629
						$options[ $key ] = $site_creation_flow;
630
					}
631
					break;
632
				case 'is_cloud_eligible':
633
					$options[ $key ] = $site->is_cloud_eligible();
634
					break;
635
			}
636
		}
637
638
		return $options;
639
	}
640
641
	protected function build_meta_response( &$response ) {
642
		$links = array(
643
			'self'     => (string) $this->links->get_site_link( $this->site->blog_id ),
644
			'help'     => (string) $this->links->get_site_link( $this->site->blog_id, 'help'      ),
645
			'posts'    => (string) $this->links->get_site_link( $this->site->blog_id, 'posts/'    ),
646
			'comments' => (string) $this->links->get_site_link( $this->site->blog_id, 'comments/' ),
647
			'xmlrpc'   => (string) $this->site->get_xmlrpc_url(),
648
		);
649
650
		$icon = $this->site->get_icon();
651
		if ( ! empty( $icon ) && ! empty( $icon['media_id'] ) ) {
652
			$links['site_icon'] = (string) $this->links->get_site_link( $this->site->blog_id, 'media/' . $icon['media_id'] );
653
		}
654
655
		$response['meta'] = (object) array(
656
			'links' => (object) $links
657
		);
658
	}
659
660
	// apply any WPCOM-only response components to a Jetpack site response
661
	public function decorate_jetpack_response( &$response ) {
662
		$this->site = $this->get_platform()->get_site( $response->ID );
663
		switch_to_blog( $this->site->get_id() );
664
665
		$wpcom_response = $this->render_response_keys( self::$jetpack_response_field_additions );
666
667
		foreach( $wpcom_response as $key => $value ) {
668
			$response->{ $key } = $value;
669
		}
670
671
		if ( $this->has_user_access() || $this->has_blog_access( $this->api->token_details ) ) {
672
			$wpcom_member_response = $this->render_response_keys( self::$jetpack_response_field_member_additions );
673
674
			foreach( $wpcom_member_response as $key => $value ) {
675
				$response->{ $key } = $value;
676
			}
677
		} else {
678
			// ensure private data is not rendered for non members of the site
679
			unset( $response->options );
680
			unset( $response->is_vip );
681
			unset( $response->single_user_site );
682
			unset( $response->is_private );
683
			unset( $response->is_coming_soon );
684
			unset( $response->capabilities );
685
			unset( $response->lang );
686
			unset( $response->user_can_manage );
687
			unset( $response->is_multisite );
688
			unset( $response->plan );
689
			unset( $response->products );
690
		}
691
692
		// render additional options
693
		if ( $response->options ) {
694
			$wpcom_options_response = $this->render_option_keys( self::$jetpack_response_option_additions );
695
696
			foreach ( $wpcom_options_response as $key => $value ) {
697
				$response->options[ $key ] = $value;
698
			}
699
		}
700
701
		restore_current_blog();
702
		return $response; // possibly no need since it's modified in place
703
	}
704
}
705
706
new WPCOM_JSON_API_List_Post_Formats_Endpoint( array(
707
	'description' => 'Get a list of post formats supported by a site.',
708
	'group'       => '__do_not_document',
709
	'stat'        => 'sites:X:post-formats',
710
711
	'method'      => 'GET',
712
	'path'        => '/sites/%s/post-formats',
713
	'path_labels' => array(
714
		'$site' => '(int|string) Site ID or domain',
715
	),
716
717
	'query_parameters' => array(
718
		'context' => false,
719
	),
720
721
	'response_format' => array(
722
		'formats' => '(object) An object of supported post formats, each key a supported format slug mapped to its display string.',
723
	)
724
) );
725
726
class WPCOM_JSON_API_List_Post_Formats_Endpoint extends WPCOM_JSON_API_Endpoint {
727
	// /sites/%s/post-formats -> $blog_id
728
	function callback( $path = '', $blog_id = 0 ) {
729
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
730
		if ( is_wp_error( $blog_id ) ) {
731
			return $blog_id;
732
		}
733
734
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
735
			$this->load_theme_functions();
736
		}
737
738
		// Get a list of supported post formats.
739
		$all_formats = get_post_format_strings();
740
		$supported   = get_theme_support( 'post-formats' );
741
742
		$supported_formats = $response['formats'] = array();
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...
743
744 View Code Duplication
		if ( isset( $supported[0] ) ) {
745
			foreach ( $supported[0] as $format ) {
746
				$supported_formats[ $format ] = $all_formats[ $format ];
747
			}
748
		}
749
750
		$response['formats'] = (object) $supported_formats;
751
752
		return $response;
753
	}
754
}
755
756
new WPCOM_JSON_API_List_Page_Templates_Endpoint( array(
757
	'description' => 'Get a list of page templates supported by a site.',
758
	'group'       => 'sites',
759
	'stat'        => 'sites:X:post-templates',
760
761
	'method'      => 'GET',
762
	'path'        => '/sites/%s/page-templates',
763
	'path_labels' => array(
764
		'$site' => '(int|string) Site ID or domain',
765
	),
766
	'query_parameters' => array(
767
		'context' => false,
768
	),
769
	'response_format' => array(
770
		'templates' => '(array) A list of supported page templates. Contains label and file.',
771
	),
772
	'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/33534099/page-templates'
773
) );
774
775
class WPCOM_JSON_API_List_Page_Templates_Endpoint extends WPCOM_JSON_API_Endpoint {
776
	// /sites/%s/page-templates -> $blog_id
777
	function callback( $path = '', $blog_id = 0 ) {
778
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
779
		if ( is_wp_error( $blog_id ) ) {
780
			return $blog_id;
781
		}
782
783
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
784
			$this->load_theme_functions();
785
		}
786
787
		$response = array();
788
		$page_templates = array();
789
790
		$templates = get_page_templates();
791
		ksort( $templates );
792
793
		foreach ( array_keys( $templates ) as $label ) {
794
			$page_templates[] = array(
795
				'label' => $label,
796
				'file'  => $templates[ $label ]
797
			);
798
		}
799
800
		$response['templates'] = $page_templates;
801
802
		return $response;
803
	}
804
}
805