Completed
Push — feature/sync-sal ( 44f866...ceeee0 )
by
unknown
16:31
created

class.wpcom-json-api-get-site-endpoint.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
3
4
	public static $site_format = array(
5
		'ID'                => '(int) Site ID',
6
		'name'              => '(string) Title of site',
7
		'description'       => '(string) Tagline or description of site',
8
		'URL'               => '(string) Full URL to the site',
9
		'user_can_manage'   => '(bool) The current user can manage this site', // deprecated
10
		'capabilities'      => '(array) Array of capabilities for the current user on this site.',
11
		'jetpack'           => '(bool)  Whether the site is a Jetpack site or not',
12
		'is_multisite'      => '(bool) Whether the site is a Multisite site or not. Always true for WP.com sites.',
13
		'post_count'        => '(int) The number of posts the site has',
14
		'subscribers_count' => '(int) The number of subscribers the site has',
15
		'lang'              => '(string) Primary language code of the site',
16
		'icon'              => '(array) An array of icon formats for the site',
17
		'logo'              => '(array) The site logo, set in the Customizer',
18
		'visible'           => '(bool) If this site is visible in the user\'s site list',
19
		'is_private'        => '(bool) If the site is a private site or not',
20
		'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.',
21
		'is_vip'            => '(bool) If the site is a VIP site or not.',
22
		'is_following'      => '(bool) If the current user is subscribed to this site in the reader',
23
		'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/',
24
		'plan'              => '(array) Details of the current plan for this site.',
25
		'updates'           => '(array) An array of available updates for plugins, themes, wordpress, and languages.',
26
		'jetpack_modules'   => '(array) A list of active Jetpack modules.',
27
		'meta'              => '(object) Meta data',
28
	);
29
30
	protected static $no_member_fields = array(
31
		'ID',
32
		'name',
33
		'description',
34
		'URL',
35
		'jetpack',
36
		'post_count',
37
		'subscribers_count',
38
		'lang',
39
		'locale',
40
		'icon',
41
		'logo',
42
		'visible',
43
		'is_private',
44
		'is_following',
45
		'meta',
46
	);
47
48
	protected static $site_options_format = array(
49
		'timezone',
50
		'gmt_offset',
51
		'videopress_enabled',
52
		'upgraded_filetypes_enabled',
53
		'login_url',
54
		'admin_url',
55
		'is_mapped_domain',
56
		'is_redirect',
57
		'unmapped_url',
58
		'featured_images_enabled',
59
		'theme_slug',
60
		'header_image',
61
		'background_color',
62
		'image_default_link_type',
63
		'image_thumbnail_width',
64
		'image_thumbnail_height',
65
		'image_thumbnail_crop',
66
		'image_medium_width',
67
		'image_medium_height',
68
		'image_large_width',
69
		'image_large_height',
70
		'permalink_structure',
71
		'post_formats',
72
		'default_post_format',
73
		'default_category',
74
		'allowed_file_types',
75
		'show_on_front',
76
		/** This filter is documented in modules/likes.php */
77
		'default_likes_enabled',
78
		'default_sharing_status',
79
		'default_comment_status',
80
		'default_ping_status',
81
		'software_version',
82
		'created_at',
83
		'wordads',
84
		'publicize_permanently_disabled',
85
		'frame_nonce',
86
		'page_on_front',
87
		'page_for_posts',
88
		'headstart',
89
		'ak_vp_bundle_enabled',
90
		'verification_services_codes',
91
		Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION,
92
		Jetpack_SEO_Titles::TITLE_FORMATS_OPTION,
93
	);
94
95
	protected static $jetpack_response_field_additions = array(
96
		'subscribers_count',
97
	);
98
99
	protected static $jetpack_response_field_member_additions = array(
100
		'capabilities',
101
		'plan',
102
	);
103
104
	protected static $jetpack_response_option_additions = array(
105
		'publicize_permanently_disabled',
106
		'ak_vp_bundle_enabled'
107
	);
108
109
	private $site;
110
111
	// protected $compact = null;
112
	protected $fields_to_include = '_all';
113
	protected $options_to_include = '_all';
114
115
	// /sites/mine
116
	// /sites/%s -> $blog_id
117
	function callback( $path = '', $blog_id = 0 ) {
118
		if ( 'mine' === $blog_id ) {
119
			$api = WPCOM_JSON_API::init();
120
			if ( ! $api->token_details || empty( $api->token_details['blog_id'] ) ) {
121
				return new WP_Error( 'authorization_required', 'An active access token must be used to query information about the current blog.', 403 );
122
			}
123
			$blog_id = $api->token_details['blog_id'];
124
		}
125
126
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
127
		if ( is_wp_error( $blog_id ) ) {
128
			return $blog_id;
129
		}
130
131
		// TODO: enable this when we can do so without being interfered with by
132
		// other endpoints that might be wrapping this one.
133
		// Uncomment and see failing test: test_jetpack_site_should_have_true_jetpack_property_via_site_meta
134
		// $this->filter_fields_and_options();
135
136
		$response = $this->build_current_site_response();
137
138
		/** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
139
		do_action( 'wpcom_json_api_objects', 'sites' );
140
141
		return $response;
142
	}
143
144
	public function filter_fields_and_options() {
145
		$query_args = $this->query_args();
146
147
		$this->fields_to_include  = empty( $query_args['fields'] ) ? '_all' : array_map( 'trim', explode( ',', $query_args['fields'] ) );
148
		$this->options_to_include = empty( $query_args['options'] ) ? '_all' : array_map( 'trim', explode( ',', $query_args['options'] ) );
149
	}
150
151
	/**
152
	 * Collects the necessary information to return for a site's response.
153
	 *
154
	 * @return (array)
155
	 */
156
	public function build_current_site_response() {
157
158
		$blog_id = (int) $this->api->get_blog_id_for_output();
159
160
		$this->site = $this->get_platform()->get_site( $blog_id );
161
162
		/**
163
 		 * Filter the structure of information about the site to return.
164
 		 *
165
 		 * @module json-api
166
 		 *
167
 		 * @since 3.9.3
168
 		 *
169
 		 * @param array $site_format Data structure.
170
 		 */
171
		$default_fields = array_keys( apply_filters( 'sites_site_format', self::$site_format ) );
172
173
		$response_keys = is_array( $this->fields_to_include ) ?
174
			array_intersect( $default_fields, $this->fields_to_include ) :
175
			$default_fields;
176
177
		if ( ! is_user_member_of_blog( get_current_user_id(), get_current_blog_id() ) ) {
178
			$response_keys = array_intersect( $response_keys, self::$no_member_fields );
179
		}
180
181
		return $this->render_response_keys( $response_keys );
182
	}
183
184
	private function render_response_keys( &$response_keys ) {
185
		$response = array();
186
187
		$is_user_logged_in = is_user_logged_in();
188
189
		$this->site->before_render();
190
191
		foreach ( $response_keys as $key ) {
192
			$this->render_response_key( $key, $response, $is_user_logged_in );
193
		}
194
195
		$this->site->after_render( $response );
196
197
		return $response;
198
	}
199
200
	protected function render_response_key( $key, &$response, $is_user_logged_in ) {
201
		do_action( 'pre_render_site_response_key', $key );
202
203
		switch ( $key ) {
204
			case 'ID' :
205
				$response[ $key ] = $this->site->blog_id;
206
				break;
207
			case 'name' :
208
				$response[ $key ] = $this->site->get_name();
209
				break;
210
			case 'description' :
211
				$response[ $key ] = $this->site->get_description();
212
				break;
213
			case 'URL' :
214
				$response[ $key ] = $this->site->get_url();
215
				break;
216
			case 'user_can_manage' :
217
				$response[ $key ] = $this->site->user_can_manage();
218
			case 'is_private' :
219
				$response[ $key ] = $this->site->is_private();
220
				break;
221
			case 'visible' :
222
				$response[ $key ] = $this->site->is_visible();
223
				break;
224
			case 'subscribers_count' :
225
				$response[ $key ] = $this->site->get_subscribers_count();
226
				break;
227
			case 'post_count' :
228
				if ( $is_user_logged_in ) {
229
					$response[ $key ] = $this->site->get_post_count();
230
				}
231
				break;
232
			case 'icon' :
233
				$icon = $this->site->get_icon();
234
235
				if ( ! is_null( $icon ) ) {
236
					$response[ $key ] = $icon;
237
				}
238
				break;
239
			case 'logo' :
240
				$response[ $key ] = $this->site->get_logo();
241
				break;
242
			case 'is_following':
243
				$response[ $key ] = $this->site->is_following();
244
				break;
245
			case 'options':
246
				// small optimisation - don't recalculate
247
				$all_options = apply_filters( 'sites_site_options_format', self::$site_options_format );
248
249
				$options_response_keys = is_array( $this->options_to_include ) ?
250
					array_intersect( $all_options, $this->options_to_include ) :
251
					$all_options;
252
253
				$options = $this->render_option_keys( $options_response_keys );
254
255
				$this->site->after_render_options( $options );
256
257
				$response[ $key ] = (object) $options;
258
				break;
259
			case 'meta':
260
				$this->build_meta_response( $response );
261
				break;
262
			case 'lang' :
263
				$response[ $key ] = $is_user_logged_in ? $this->site->get_locale() : false;
264
				break;
265
			case 'locale' :
266
				$response[ $key ] = $is_user_logged_in ? $this->site->get_locale() : false;
267
				break;
268
			case 'jetpack' :
269
				$response[ $key ] = $this->site->is_jetpack();
270
				break;
271
			case 'single_user_site' :
272
				$response[ $key ] = $this->site->is_single_user_site();
0 ignored issues
show
The method is_single_user_site() cannot be called from this context as it is declared protected in class Jetpack_Site.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
273
				break;
274
			case 'is_vip' :
275
				$response[ $key ] = $this->site->is_vip();
276
				break;
277
			case 'is_multisite' :
278
				$response[ $key ] = $this->site->is_multisite();
0 ignored issues
show
The method is_multisite() cannot be called from this context as it is declared protected in class Jetpack_Site.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
279
				break;
280
			case 'capabilities' :
281
				$response[ $key ] = $this->site->get_capabilities();
282
				break;
283
			case 'jetpack_modules':
284
				$jetpack_modules = $this->site->get_jetpack_modules();
285
				if ( ! is_null( $jetpack_modules ) ) {
286
					$response[ $key ] = $jetpack_modules;
287
				}
288
				break;
289
			case 'plan' :
290
				$response[ $key ] = $this->site->get_plan();
291
				break;
292
		}
293
294
		do_action( 'post_render_site_response_key', $key );
295
	}
296
297
	protected function render_option_keys( &$options_response_keys ) {
298
		if ( ! current_user_can( 'edit_posts' ) ) {
299
			return array();
300
		}
301
302
		$options = array();
303
		$site = $this->site;
304
305
		$custom_front_page = $site->is_custom_front_page();
306
307
308
		foreach ( $options_response_keys as $key ) {
309
			switch ( $key ) {
310
				case 'timezone' :
311
					$options[ $key ] = $site->get_timezone();
312
					break;
313
				case 'gmt_offset' :
314
					$options[ $key ] = $site->get_gmt_offset();
315
					break;
316
				case 'videopress_enabled' :
317
					$options[ $key ] = $site->has_videopress();
318
					break;
319
				case 'upgraded_filetypes_enabled' :
320
					$options[ $key ] = $site->upgraded_filetypes_enabled();
321
					break;
322
				case 'login_url' :
323
					$options[ $key ] = $site->get_login_url();
324
					break;
325
				case 'admin_url' :
326
					$options[ $key ] = $site->get_admin_url();
327
					break;
328
				case 'is_mapped_domain' :
329
					$options[ $key ] = $site->is_mapped_domain();
330
					break;
331
				case 'is_redirect' :
332
					$options[ $key ] = $site->is_redirect();
333
					break;
334
				case 'unmapped_url' :
335
					$options[ $key ] = $site->get_unmapped_url();
336
					break;
337
				case 'featured_images_enabled' :
338
					$options[ $key ] = $site->featured_images_enabled();
339
					break;
340
				case 'theme_slug' :
341
					$options[ $key ] = $site->get_theme_slug();
342
					break;
343
				case 'header_image' :
344
					$options[ $key ] = $site->get_header_image();
345
					break;
346
				case 'background_color' :
347
					$options[ $key ] = $site->get_background_color();
348
					break;
349
				case 'image_default_link_type' :
350
					$options[ $key ] = $site->get_image_default_link_type();
351
					break;
352
				case 'image_thumbnail_width' :
353
					$options[ $key ] = $site->get_image_thumbnail_width();
354
					break;
355
				case 'image_thumbnail_height' :
356
					$options[ $key ] = $site->get_image_thumbnail_height();
357
					break;
358
				case 'image_thumbnail_crop' :
359
					$options[ $key ] = $site->get_image_thumbnail_crop();
360
					break;
361
				case 'image_medium_width' :
362
					$options[ $key ] = $site->get_image_medium_width();
363
					break;
364
				case 'image_medium_height' :
365
					$options[ $key ] = $site->get_image_medium_height();
366
					break;
367
				case 'image_large_width' :
368
					$options[ $key ] = $site->get_image_large_width();
369
					break;
370
				case 'image_large_height' :
371
					$options[ $key ] = $site->get_image_large_height();
372
					break;
373
				case 'permalink_structure' :
374
					$options[ $key ] = $site->get_permalink_structure();
375
					break;
376
				case 'post_formats' :
377
					$options[ $key ] = $site->get_post_formats();
378
					break;
379
				case 'default_post_format' :
380
					$options[ $key ] = $site->get_default_post_format();
381
					break;
382
				case 'default_category' :
383
					$options[ $key ] = $site->get_default_category();
384
					break;
385
				case 'allowed_file_types' :
386
					$options[ $key ] = $site->allowed_file_types();
387
					break;
388
				case 'show_on_front' :
389
					$options[ $key ] = $site->get_show_on_front();
390
					break;
391
				/** This filter is documented in modules/likes.php */
392
				case 'default_likes_enabled' :
393
					$options[ $key ] = $site->get_default_likes_enabled();
394
					break;
395
				case 'default_sharing_status' :
396
					$options[ $key ] = $site->get_default_sharing_status();
397
					break;
398
				case 'default_comment_status' :
399
					$options[ $key ] = $site->get_default_comment_status();
400
					break;
401
				case 'default_ping_status' :
402
					$options[ $key ] = $site->default_ping_status();
403
					break;
404
				case 'software_version' :
405
					$options[ $key ] = $site->get_wordpress_version();
406
					break;
407
				case 'created_at' :
408
					$options[ $key ] = $site->get_registered_date();
409
					break;
410
				case 'wordads' :
411
					$options[ $key ] = $site->has_wordads();
412
					break;
413
				case 'publicize_permanently_disabled' :
414
					$options[ $key ] = $site->is_publicize_permanently_disabled();
415
					break;
416
				case 'frame_nonce' :
417
					$options[ $key ] = $site->get_frame_nonce();
418
					break;
419
				case 'page_on_front' :
420
					if ( $custom_front_page ) {
421
						$options[ $key ] = $site->get_page_on_front();
422
					}
423
					break;
424
				case 'page_for_posts' :
425
					if ( $custom_front_page ) {
426
						$options[ $key ] = $site->get_page_for_posts();
427
					}
428
					break;
429
				case 'headstart' :
430
					$options[ $key ] = $site->is_headstart();
431
					break;
432
				case 'ak_vp_bundle_enabled' :
433
					$options[ $key ] = $site->get_ak_vp_bundle_enabled();
434
					break;
435
				case Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION :
436
					$options[ $key ] = $site->get_jetpack_seo_front_page_description();
437
					break;
438
				case Jetpack_SEO_Titles::TITLE_FORMATS_OPTION :
439
					$options[ $key ] = $site->get_jetpack_seo_title_formats();
440
					break;
441
				case 'verification_services_codes' :
442
					$options[ $key ] = $site->get_verification_services_codes();
443
					break;
444
			}
445
		}
446
447
		return $options;
448
	}
449
450
	protected function build_meta_response( &$response ) {
451
		$links = array(
452
			'self'     => (string) $this->links->get_site_link( $this->site->blog_id ),
453
			'help'     => (string) $this->links->get_site_link( $this->site->blog_id, 'help'      ),
454
			'posts'    => (string) $this->links->get_site_link( $this->site->blog_id, 'posts/'    ),
455
			'comments' => (string) $this->links->get_site_link( $this->site->blog_id, 'comments/' ),
456
			'xmlrpc'   => (string) $this->site->get_xmlrpc_url(),
457
		);
458
459
		$icon = $this->site->get_icon();
460
		if ( ! empty( $icon ) && ! empty( $icon['media_id'] ) ) {
461
			$links['site_icon'] = (string) $this->links->get_site_link( $this->site->blog_id, 'media/' . $icon['media_id'] );
462
		}
463
464
		$response['meta'] = (object) array(
465
			'links' => (object) $links
466
		);
467
	}
468
469
	// apply any WPCOM-only response components to a Jetpack site response
470
	public function decorate_jetpack_response( &$response ) {
471
		$this->site = $this->get_platform()->get_site( $response->ID );
472
473
		// ensure the response is marked as being from Jetpack
474
		$response->jetpack = true;
475
476
		$wpcom_response = $this->render_response_keys( self::$jetpack_response_field_additions );
477
478
		foreach( $wpcom_response as $key => $value ) {
479
			$response->{ $key } = $value;
480
		}
481
482
		$token_details = (object) $this->api->token_details;
483
		if ( is_user_member_of_blog( get_current_user_id(), get_current_blog_id() ) || 'blog' === $token_details->access ) {
484
			$wpcom_member_response = $this->render_response_keys( self::$jetpack_response_field_member_additions );
485
486
			foreach( $wpcom_member_response as $key => $value ) {
487
				$response->{ $key } = $value;
488
			}
489
		} else {
490
			// ensure private data is not rendered for non members of the site
491
			unset( $response->options );
492
			unset( $response->is_vip );
493
			unset( $response->single_user_site );
494
			unset( $response->is_private );
495
			unset( $response->capabilities );
496
			unset( $response->lang );
497
			unset( $response->user_can_manage );
498
			unset( $response->is_multisite );
499
			unset( $response->plan );
500
		}
501
502
		// render additional options
503
		if ( $response->options ) {
504
			$wpcom_options_response = $this->render_option_keys( self::$jetpack_response_option_additions );
505
506
			foreach ( $wpcom_options_response as $key => $value ) {
507
				$response->options[ $key ] = $value;
508
			}
509
		}
510
511
		return $response; // possibly no need since it's modified in place
512
	}
513
}
514
515
class WPCOM_JSON_API_List_Post_Formats_Endpoint extends WPCOM_JSON_API_Endpoint {
516
	// /sites/%s/post-formats -> $blog_id
517
	function callback( $path = '', $blog_id = 0 ) {
518
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
519
		if ( is_wp_error( $blog_id ) ) {
520
			return $blog_id;
521
		}
522
523
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
524
			$this->load_theme_functions();
525
		}
526
527
		// Get a list of supported post formats.
528
		$all_formats = get_post_format_strings();
529
		$supported   = get_theme_support( 'post-formats' );
530
531
		$supported_formats = $response['formats'] = array();
532
533 View Code Duplication
		if ( isset( $supported[0] ) ) {
534
			foreach ( $supported[0] as $format ) {
535
				$supported_formats[ $format ] = $all_formats[ $format ];
536
			}
537
		}
538
539
		$response['formats'] = (object) $supported_formats;
540
541
		return $response;
542
	}
543
}
544
545
class WPCOM_JSON_API_List_Page_Templates_Endpoint extends WPCOM_JSON_API_Endpoint {
546
	// /sites/%s/page-templates -> $blog_id
547
	function callback( $path = '', $blog_id = 0 ) {
548
		$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
549
		if ( is_wp_error( $blog_id ) ) {
550
			return $blog_id;
551
		}
552
553
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
554
			$this->load_theme_functions();
555
		}
556
557
		$response = array();
558
		$page_templates = array();
559
560
		$templates = get_page_templates();
561
		ksort( $templates );
562
563
		foreach ( array_keys( $templates ) as $label ) {
564
			$page_templates[] = array(
565
				'label' => $label,
566
				'file'  => $templates[ $label ]
567
			);
568
		}
569
570
		$response['templates'] = $page_templates;
571
572
		return $response;
573
	}
574
}
575