PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 13.1.5
Jetpack – WP Security, Backup, Speed, & Growth v13.1.5
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / json-endpoints / class.wpcom-json-api-get-site-endpoint.php
class.wpcom-json-api-get-site-endpoint.php
1,143 lines 35.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 new WPCOM_JSON_API_GET_Site_Endpoint(
4 array(
5 'description' => 'Get information about a site.',
6 'group' => 'sites',
7 'stat' => 'sites:X',
8 'allowed_if_flagged' => true,
9 'method' => 'GET',
10 'max_version' => '1.1',
11 'new_version' => '1.2',
12 'path' => '/sites/%s',
13 'path_labels' => array(
14 '$site' => '(int|string) Site ID or domain',
15 ),
16 'allow_jetpack_site_auth' => true,
17
18 'allow_fallback_to_jetpack_blog_token' => true,
19
20 'query_parameters' => array(
21 'context' => false,
22 'options' => '(string) Optional. Returns specified options only. Comma-separated list. Example: options=login_url,timezone',
23 ),
24
25 'response_format' => WPCOM_JSON_API_GET_Site_Endpoint::$site_format,
26
27 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/en.blog.wordpress.com/',
28 )
29 );
30
31 /**
32 * GET Site endpoint class.
33 */
34 class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
35
36 /**
37 * Site meta data.
38 *
39 * @var array $site_format
40 */
41 public static $site_format = array(
42 'ID' => '(int) Site ID',
43 'name' => '(string) Title of site',
44 'description' => '(string) Tagline or description of site',
45 'URL' => '(string) Full URL to the site',
46 'user_can_manage' => '(bool) The current user can manage this site', // deprecated.
47 'capabilities' => '(array) Array of capabilities for the current user on this site.',
48 'jetpack' => '(bool) Whether the site is a Jetpack site or not',
49 'jetpack_connection' => '(bool) Whether the site is connected to WP.com via `jetpack-connection`',
50 'is_multisite' => '(bool) Whether the site is a Multisite site or not. Always true for WP.com sites.',
51 'site_owner' => '(int) User ID of the site owner',
52 'post_count' => '(int) The number of posts the site has',
53 'subscribers_count' => '(int) The number of subscribers the site has',
54 'lang' => '(string) Primary language code of the site',
55 'icon' => '(array) An array of icon formats for the site',
56 'logo' => '(array) The site logo, set in the Customizer',
57 'visible' => '(bool) If this site is visible in the user\'s site list',
58 'is_private' => '(bool) If the site is a private site or not',
59 'is_coming_soon' => '(bool) If the site is marked as "coming soon" or not',
60 '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.',
61 'is_vip' => '(bool) If the site is a VIP site or not.',
62 'is_following' => '(bool) If the current user is subscribed to this site in the reader',
63 'organization_id' => '(int) P2 Organization identifier.',
64 '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/',
65 'p2_thumbnail_elements' => '(array) Details used to render a thumbnail of the site. P2020 themed sites only.',
66 'plan' => '(array) Details of the current plan for this site.',
67 'products' => '(array) Details of the current products for this site.',
68 'zendesk_site_meta' => '(array) Site meta data for Zendesk.',
69 'updates' => '(array) An array of available updates for plugins, themes, wordpress, and languages.',
70 'jetpack_modules' => '(array) A list of active Jetpack modules.',
71 'meta' => '(object) Meta data',
72 'quota' => '(array) An array describing how much space a user has left for uploads',
73 'launch_status' => '(string) A string describing the launch status of a site',
74 'site_migration' => '(array) Data about any migration into the site.',
75 'is_fse_active' => '(bool) If the site has Full Site Editing active or not.',
76 'is_fse_eligible' => '(bool) If the site is capable of Full Site Editing or not',
77 'is_core_site_editor_enabled' => '(bool) If the site has the core site editor enabled.',
78 'is_wpcom_atomic' => '(bool) If the site is a WP.com Atomic one.',
79 'is_wpcom_staging_site' => '(bool) If the site is a WP.com staging site.',
80 'user_interactions' => '(array) An array of user interactions with a site.',
81 'was_ecommerce_trial' => '(bool) If the site ever used an eCommerce trial.',
82 'was_upgraded_from_trial' => '(bool) If the site ever upgraded to a paid plan from a trial.',
83 'was_migration_trial' => '(bool) If the site ever used a migration trial.',
84 'was_hosting_trial' => '(bool) If the site ever used a hosting trial.',
85 'wpcom_site_setup' => '(string) The WP.com site setup identifier.',
86 );
87
88 /**
89 * No member fields.
90 *
91 * @var array $no_member_fields
92 */
93 protected static $no_member_fields = array(
94 'ID',
95 'name',
96 'description',
97 'URL',
98 'jetpack',
99 'jetpack_connection',
100 'post_count',
101 'subscribers_count',
102 'lang',
103 'locale',
104 'icon',
105 'logo',
106 'visible',
107 'is_private',
108 'is_coming_soon',
109 'is_following',
110 'organization_id',
111 'meta',
112 'launch_status',
113 'site_migration',
114 'is_fse_active',
115 'is_fse_eligible',
116 'is_core_site_editor_enabled',
117 'is_wpcom_atomic',
118 'is_wpcom_staging_site',
119 );
120
121 /**
122 * Site options.
123 *
124 * @var array $site_options_format
125 */
126 protected static $site_options_format = array(
127 'timezone',
128 'gmt_offset',
129 'blog_public',
130 'videopress_enabled',
131 'upgraded_filetypes_enabled',
132 'login_url',
133 'admin_url',
134 'is_mapped_domain',
135 'is_redirect',
136 'unmapped_url',
137 'featured_images_enabled',
138 'theme_slug',
139 'header_image',
140 'background_color',
141 'image_default_link_type',
142 'image_thumbnail_width',
143 'image_thumbnail_height',
144 'image_thumbnail_crop',
145 'image_medium_width',
146 'image_medium_height',
147 'image_large_width',
148 'image_large_height',
149 'permalink_structure',
150 'post_formats',
151 'default_post_format',
152 'default_category',
153 'allowed_file_types',
154 'show_on_front',
155 /** This filter is documented in modules/likes.php */
156 'default_likes_enabled',
157 'default_sharing_status',
158 'default_comment_status',
159 'default_ping_status',
160 'software_version',
161 'created_at',
162 'updated_at',
163 'wordads',
164 'publicize_permanently_disabled',
165 'frame_nonce',
166 'jetpack_frame_nonce',
167 'page_on_front',
168 'page_for_posts',
169 'headstart',
170 'headstart_is_fresh',
171 'ak_vp_bundle_enabled',
172 Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION,
173 Jetpack_SEO_Titles::TITLE_FORMATS_OPTION,
174 'verification_services_codes',
175 'podcasting_archive',
176 'is_domain_only',
177 'is_automated_transfer',
178 'is_wpcom_atomic',
179 'is_wpcom_store',
180 'signup_is_store',
181 'has_pending_automated_transfer',
182 'woocommerce_is_active',
183 'editing_toolkit_is_active',
184 'design_type',
185 'site_goals',
186 'site_segment',
187 'site_source_slug',
188 'import_engine',
189 'is_pending_plan',
190 'is_wpforteams_site',
191 'p2_hub_blog_id',
192 'site_creation_flow',
193 'is_cloud_eligible',
194 'selected_features',
195 'anchor_podcast',
196 'was_created_with_blank_canvas_design',
197 'videopress_storage_used',
198 'is_difm_lite_in_progress',
199 'difm_lite_site_options',
200 'site_intent',
201 'site_vertical_id',
202 'blogging_prompts_settings',
203 'launchpad_screen',
204 'launchpad_checklist_tasks_statuses',
205 'wpcom_production_blog_id',
206 'wpcom_staging_blog_ids',
207 'can_blaze',
208 'wpcom_site_setup',
209 'is_commercial',
210 'wpcom_admin_interface',
211 );
212
213 /**
214 * List of DIFM Lite options to be displayed
215 *
216 * @var array $displayed_difm_lite_site_options
217 */
218 protected static $displayed_difm_lite_site_options = array(
219 'site_category',
220 'is_website_content_submitted',
221 'selected_page_titles',
222 );
223
224 /**
225 * Jetpack response fields.
226 *
227 * @var array $jetpack_response_field_additions
228 */
229 protected static $jetpack_response_field_additions = array(
230 'subscribers_count',
231 'site_migration',
232 'site_owner',
233 'is_wpcom_staging_site',
234 'was_ecommerce_trial',
235 'was_migration_trial',
236 'was_hosting_trial',
237 'was_upgraded_from_trial',
238 );
239
240 /**
241 * Jetpack response field member additions.
242 *
243 * @var array $jetpack_response_field_member_additions
244 */
245 protected static $jetpack_response_field_member_additions = array(
246 'capabilities',
247 'plan',
248 'products',
249 'zendesk_site_meta',
250 );
251
252 /**
253 * Jetpack response option additions.
254 *
255 * @var array $jetpack_response_field_member_additions
256 */
257 protected static $jetpack_response_option_additions = array(
258 'publicize_permanently_disabled',
259 'ak_vp_bundle_enabled',
260 'is_automated_transfer',
261 'is_wpcom_atomic',
262 'is_wpcom_store',
263 'woocommerce_is_active',
264 'editing_toolkit_is_active',
265 'frame_nonce',
266 'jetpack_frame_nonce',
267 'design_type',
268 'wordads',
269 // Use the site registered date from wpcom, since it is only available in a multisite context
270 // and defaults to `0000-00-00T00:00:00+00:00` from the Jetpack site.
271 // See https://github.com/Automattic/jetpack/blob/58638f46094b36f5df9cbc4570006544f0ad300c/sal/class.json-api-site-base.php#L387.
272 'created_at',
273 'updated_at',
274 'is_pending_plan',
275 'is_cloud_eligible',
276 'videopress_storage_used',
277 'blogging_prompts_settings',
278 'wpcom_production_blog_id',
279 'wpcom_staging_blog_ids',
280 'is_commercial',
281 'wpcom_admin_interface',
282 );
283
284 /**
285 * Current enabled trials.
286 *
287 * @var array $jetpack_enabled_trials
288 */
289 public static $jetpack_enabled_trials = array(
290 'was_ecommerce_trial' => 'ecommerce',
291 'was_migration_trial' => 'migration',
292 'was_hosting_trial' => 'hosting',
293 );
294
295 /**
296 * Site
297 *
298 * @var $site.
299 */
300 private $site;
301
302 /**
303 * Fields to include.
304 *
305 * @var $fields_to_include
306 */
307 protected $fields_to_include = '_all';
308
309 /**
310 * Options to include.
311 *
312 * @var $options_to_include
313 */
314 protected $options_to_include = '_all';
315
316 /**
317 *
318 * API callback.
319 *
320 * /sites/mine
321 * /sites/%s -> $blog_id\
322 *
323 * @param string $path - the path.
324 * @param int $blog_id - the blog ID.
325 */
326 public function callback( $path = '', $blog_id = 0 ) {
327 if ( 'mine' === $blog_id ) {
328 $api = WPCOM_JSON_API::init();
329 if ( ! $api->token_details || empty( $api->token_details['blog_id'] ) ) {
330 return new WP_Error( 'authorization_required', 'An active access token must be used to query information about the current blog.', 403 );
331 }
332 $blog_id = $api->token_details['blog_id'];
333 }
334
335 add_filter( 'wpcom_allow_jetpack_blog_token', '__return_true' );
336 $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
337 if ( is_wp_error( $blog_id ) ) {
338 return $blog_id;
339 }
340
341 $this->filter_fields_and_options();
342
343 $response = $this->build_current_site_response();
344
345 /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
346 do_action( 'wpcom_json_api_objects', 'sites' );
347
348 return $response;
349 }
350
351 /**
352 * Filter fields and options.
353 */
354 public function filter_fields_and_options() {
355 $query_args = $this->query_args();
356
357 $this->fields_to_include = empty( $query_args['fields'] ) ? '_all' : array_map( 'trim', explode( ',', $query_args['fields'] ) );
358 $this->options_to_include = empty( $query_args['options'] ) ? '_all' : array_map( 'trim', explode( ',', $query_args['options'] ) );
359 }
360
361 /**
362 * Collects the necessary information to return for a site's response.
363 *
364 * @return array
365 */
366 public function build_current_site_response() {
367
368 $blog_id = (int) $this->api->get_blog_id_for_output();
369
370 $this->site = $this->get_platform()->get_site( $blog_id );
371
372 /**
373 * Filter the structure of information about the site to return.
374 *
375 * @module json-api
376 *
377 * @since 3.9.3
378 *
379 * @param array $site_format Data structure.
380 */
381 $default_fields = array_keys( apply_filters( 'sites_site_format', self::$site_format ) );
382
383 $response_keys = is_array( $this->fields_to_include ) ?
384 array_intersect( $default_fields, $this->fields_to_include ) :
385 $default_fields;
386
387 $has_blog_access = $this->has_blog_access( $this->api->token_details );
388 $has_user_access = $this->has_user_access();
389
390 if ( ! $has_user_access && ! $has_blog_access ) {
391 // Public access without user or blog auth, only return `$no_member_fields`.
392 $response_keys = array_intersect( $response_keys, self::$no_member_fields );
393 } elseif ( $has_user_access && ! current_user_can( 'edit_posts' ) ) {
394 // Subscriber level user, don't return site options.
395 $response_keys = array_diff( $response_keys, array( 'options' ) );
396 }
397
398 return $this->render_response_keys( $response_keys );
399 }
400
401 /**
402 * Checks that the current user has access to the current blog.
403 *
404 * @return bool Whether or not the current user can access the current blog.
405 */
406 private function has_user_access() {
407 return is_user_member_of_blog( get_current_user_id(), get_current_blog_id() );
408 }
409
410 /**
411 * Checks if the request has a valid blog token for the current blog.
412 *
413 * @param array $token_details Access token for the api request.
414 * @return bool
415 */
416 private function has_blog_access( $token_details ) {
417 $token_details = (array) $token_details;
418 if ( ! isset( $token_details['access'] ) || ! isset( $token_details['auth'] ) || ! isset( $token_details['blog_id'] ) ) {
419 return false;
420 }
421
422 return 'jetpack' === $token_details['auth'] &&
423 'blog' === $token_details['access'] &&
424 get_current_blog_id() === $token_details['blog_id'];
425 }
426
427 /**
428 * Render response keys.
429 *
430 * @param array $response_keys - the response keys.
431 */
432 private function render_response_keys( &$response_keys ) {
433 $response = array();
434
435 $is_user_logged_in = is_user_logged_in();
436
437 $this->site->before_render();
438
439 foreach ( $response_keys as $key ) {
440 $this->render_response_key( $key, $response, $is_user_logged_in );
441 }
442
443 $this->site->after_render( $response );
444
445 return $response;
446 }
447
448 /**
449 * Render response key.
450 *
451 * @param string $key - the key.
452 * @param array $response - the response.
453 * @param boolean $is_user_logged_in - if the user is logged in.
454 */
455 protected function render_response_key( $key, &$response, $is_user_logged_in ) {
456 do_action( 'pre_render_site_response_key', $key );
457
458 switch ( $key ) {
459 case 'ID':
460 $response[ $key ] = $this->site->blog_id;
461 break;
462 case 'name':
463 $response[ $key ] = $this->site->get_name();
464 break;
465 case 'description':
466 $response[ $key ] = $this->site->get_description();
467 break;
468 case 'URL':
469 $response[ $key ] = $this->site->get_url();
470 break;
471 case 'user_can_manage':
472 $response[ $key ] = $this->site->user_can_manage();
473 // fall through is intentional.
474 case 'is_private':
475 $response[ $key ] = $this->site->is_private();
476 break;
477 case 'is_coming_soon':
478 // This option is stored on wp.com for both simple and atomic sites. @see mu-plugins/private-blog.php.
479 $response[ $key ] = $this->site->is_coming_soon();
480
481 break;
482 case 'launch_status':
483 $response[ $key ] = $this->site->get_launch_status();
484 break;
485 case 'visible':
486 $response[ $key ] = $this->site->is_visible();
487 break;
488 case 'subscribers_count':
489 $response[ $key ] = $this->site->get_subscribers_count();
490 break;
491 case 'post_count':
492 if ( $is_user_logged_in ) {
493 $response[ $key ] = $this->site->get_post_count();
494 }
495 break;
496 case 'icon':
497 $icon = $this->site->get_icon();
498
499 if ( $icon !== null ) {
500 $response[ $key ] = $icon;
501 }
502 break;
503 case 'logo':
504 $response[ $key ] = $this->site->get_logo();
505 break;
506 case 'is_following':
507 $response[ $key ] = $this->site->is_following();
508 break;
509 case 'options':
510 // small optimisation - don't recalculate.
511 $all_options = apply_filters( 'sites_site_options_format', self::$site_options_format );
512
513 $options_response_keys = is_array( $this->options_to_include ) ?
514 array_intersect( $all_options, $this->options_to_include ) :
515 $all_options;
516
517 $options = $this->render_option_keys( $options_response_keys );
518
519 $this->site->after_render_options( $options );
520
521 $response[ $key ] = (object) $options;
522 break;
523 case 'meta':
524 $this->build_meta_response( $response );
525 break;
526 case 'lang':
527 $response[ $key ] = $is_user_logged_in ? $this->site->get_locale() : false;
528 break;
529 case 'locale':
530 $response[ $key ] = $is_user_logged_in ? $this->site->get_locale() : false;
531 break;
532 case 'jetpack':
533 $response[ $key ] = $this->site->is_jetpack();
534 break;
535 case 'jetpack_connection':
536 $response[ $key ] = $this->site->is_jetpack_connection();
537 break;
538 case 'single_user_site':
539 $response[ $key ] = $this->site->is_single_user_site();
540 break;
541 case 'is_vip':
542 $response[ $key ] = $this->site->is_vip();
543 break;
544 case 'is_multisite':
545 $response[ $key ] = $this->site->is_multisite();
546 break;
547 case 'site_owner':
548 $response[ $key ] = $this->site->get_site_owner();
549 break;
550 case 'organization_id':
551 $response[ $key ] = $this->site->get_p2_organization_id();
552 break;
553
554 case 'capabilities':
555 $response[ $key ] = $this->site->get_capabilities();
556 break;
557 case 'jetpack_modules':
558 if ( is_user_member_of_blog() ) {
559 $response[ $key ] = $this->site->get_jetpack_modules();
560 }
561 break;
562 case 'plan':
563 $response[ $key ] = $this->site->get_plan();
564 break;
565 case 'products':
566 $response[ $key ] = $this->site->get_products();
567 break;
568 case 'zendesk_site_meta':
569 // D59613-code only added this function to the wpcom SAL subclasses. Absent any better idea,
570 // we'll just omit the key entirely in Jetpack.
571 if ( is_callable( array( $this->site, 'get_zendesk_site_meta' ) ) ) {
572 $response[ $key ] = $this->site->get_zendesk_site_meta();
573 }
574 break;
575 case 'quota':
576 $response[ $key ] = $this->site->get_quota();
577 break;
578 case 'site_migration':
579 $response[ $key ] = $this->site->get_migration_meta();
580 break;
581 case 'is_fse_active':
582 $response[ $key ] = $this->site->is_fse_active();
583 break;
584 case 'is_fse_eligible':
585 $response[ $key ] = $this->site->is_fse_eligible();
586 break;
587 case 'is_core_site_editor_enabled':
588 $response[ $key ] = $this->site->is_core_site_editor_enabled();
589 break;
590 case 'is_wpcom_atomic':
591 $response[ $key ] = $this->site->is_wpcom_atomic();
592 break;
593 case 'is_wpcom_staging_site':
594 $response[ $key ] = $this->site->is_wpcom_staging_site();
595 break;
596 case 'user_interactions':
597 $response[ $key ] = $this->site->get_user_interactions();
598 break;
599 case 'p2_thumbnail_elements':
600 $response[ $key ] = $this->site->get_p2_thumbnail_elements();
601 break;
602 case 'was_ecommerce_trial':
603 $response[ $key ] = $this->site->was_trial( self::$jetpack_enabled_trials['was_ecommerce_trial'] );
604 break;
605 case 'was_migration_trial':
606 $response[ $key ] = $this->site->was_trial( self::$jetpack_enabled_trials['was_migration_trial'] );
607 break;
608 case 'was_hosting_trial':
609 $response[ $key ] = $this->site->was_trial( self::$jetpack_enabled_trials['was_hosting_trial'] );
610 break;
611 case 'was_upgraded_from_trial':
612 $response[ $key ] = $this->site->was_upgraded_from_trial();
613 break;
614 }
615
616 do_action( 'post_render_site_response_key', $key );
617 }
618
619 /**
620 * Render option keys.
621 *
622 * @param array $options_response_keys - the response keys.
623 */
624 protected function render_option_keys( &$options_response_keys ) {
625 $options = array();
626 $site = $this->site;
627
628 $custom_front_page = $site->is_custom_front_page();
629
630 foreach ( $options_response_keys as $key ) {
631 switch ( $key ) {
632 case 'timezone':
633 $options[ $key ] = $site->get_timezone();
634 break;
635 case 'gmt_offset':
636 $options[ $key ] = $site->get_gmt_offset();
637 break;
638 case 'videopress_enabled':
639 $options[ $key ] = $site->has_videopress();
640 break;
641 case 'upgraded_filetypes_enabled':
642 $options[ $key ] = $site->upgraded_filetypes_enabled();
643 break;
644 case 'login_url':
645 $options[ $key ] = $site->get_login_url();
646 break;
647 case 'admin_url':
648 $options[ $key ] = $site->get_admin_url();
649 break;
650 case 'is_mapped_domain':
651 $options[ $key ] = $site->is_mapped_domain();
652 break;
653 case 'is_redirect':
654 $options[ $key ] = $site->is_redirect();
655 break;
656 case 'unmapped_url':
657 $options[ $key ] = $site->get_unmapped_url();
658 break;
659 case 'featured_images_enabled':
660 $options[ $key ] = $site->featured_images_enabled();
661 break;
662 case 'theme_slug':
663 $options[ $key ] = $site->get_theme_slug();
664 break;
665 case 'header_image':
666 $options[ $key ] = $site->get_header_image();
667 break;
668 case 'background_color':
669 $options[ $key ] = $site->get_background_color();
670 break;
671 case 'image_default_link_type':
672 $options[ $key ] = $site->get_image_default_link_type();
673 break;
674 case 'image_thumbnail_width':
675 $options[ $key ] = $site->get_image_thumbnail_width();
676 break;
677 case 'image_thumbnail_height':
678 $options[ $key ] = $site->get_image_thumbnail_height();
679 break;
680 case 'image_thumbnail_crop':
681 $options[ $key ] = $site->get_image_thumbnail_crop();
682 break;
683 case 'image_medium_width':
684 $options[ $key ] = $site->get_image_medium_width();
685 break;
686 case 'image_medium_height':
687 $options[ $key ] = $site->get_image_medium_height();
688 break;
689 case 'image_large_width':
690 $options[ $key ] = $site->get_image_large_width();
691 break;
692 case 'image_large_height':
693 $options[ $key ] = $site->get_image_large_height();
694 break;
695 case 'permalink_structure':
696 $options[ $key ] = $site->get_permalink_structure();
697 break;
698 case 'post_formats':
699 $options[ $key ] = $site->get_post_formats();
700 break;
701 case 'default_post_format':
702 $options[ $key ] = $site->get_default_post_format();
703 break;
704 case 'default_category':
705 $options[ $key ] = $site->get_default_category();
706 break;
707 case 'allowed_file_types':
708 $options[ $key ] = $site->allowed_file_types();
709 break;
710 case 'show_on_front':
711 $options[ $key ] = $site->get_show_on_front();
712 break;
713 /** This filter is documented in modules/likes.php */
714 case 'default_likes_enabled':
715 $options[ $key ] = $site->get_default_likes_enabled();
716 break;
717 case 'default_sharing_status':
718 $options[ $key ] = $site->get_default_sharing_status();
719 break;
720 case 'default_comment_status':
721 $options[ $key ] = $site->get_default_comment_status();
722 break;
723 case 'default_ping_status':
724 $options[ $key ] = $site->default_ping_status();
725 break;
726 case 'software_version':
727 $options[ $key ] = $site->get_wordpress_version();
728 break;
729 case 'created_at':
730 $options[ $key ] = $site->get_registered_date();
731 break;
732 case 'updated_at':
733 $options[ $key ] = $site->get_last_update_date();
734 break;
735 case 'wordads':
736 $options[ $key ] = $site->has_wordads();
737 break;
738 case 'publicize_permanently_disabled':
739 $options[ $key ] = $site->is_publicize_permanently_disabled();
740 break;
741 case 'frame_nonce':
742 $options[ $key ] = $site->get_frame_nonce();
743 break;
744 case 'jetpack_frame_nonce':
745 $options[ $key ] = $site->get_jetpack_frame_nonce();
746 break;
747 case 'page_on_front':
748 if ( $custom_front_page ) {
749 $options[ $key ] = $site->get_page_on_front();
750 }
751 break;
752 case 'page_for_posts':
753 if ( $custom_front_page ) {
754 $options[ $key ] = $site->get_page_for_posts();
755 }
756 break;
757 case 'headstart':
758 $options[ $key ] = $site->is_headstart();
759 break;
760 case 'headstart_is_fresh':
761 $options[ $key ] = $site->is_headstart_fresh();
762 break;
763 case 'ak_vp_bundle_enabled':
764 $options[ $key ] = $site->get_ak_vp_bundle_enabled();
765 break;
766 case Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION:
767 $options[ $key ] = $site->get_jetpack_seo_front_page_description();
768 break;
769 case Jetpack_SEO_Titles::TITLE_FORMATS_OPTION:
770 $options[ $key ] = $site->get_jetpack_seo_title_formats();
771 break;
772 case 'verification_services_codes':
773 $options[ $key ] = $site->get_verification_services_codes();
774 break;
775 case 'podcasting_archive':
776 $options[ $key ] = $site->get_podcasting_archive();
777 break;
778 case 'is_domain_only':
779 $options[ $key ] = $site->is_domain_only();
780 break;
781 case 'is_automated_transfer':
782 $options[ $key ] = $site->is_automated_transfer();
783 break;
784 case 'blog_public':
785 $options[ $key ] = $site->get_blog_public();
786 break;
787 case 'is_wpcom_atomic':
788 $options[ $key ] = $site->is_wpcom_atomic();
789 break;
790 case 'is_wpcom_store':
791 $options[ $key ] = $site->is_wpcom_store();
792 break;
793 case 'signup_is_store':
794 $signup_is_store = $site->signup_is_store();
795
796 if ( $signup_is_store ) {
797 $options[ $key ] = $site->signup_is_store();
798 }
799
800 break;
801 case 'has_pending_automated_transfer':
802 $has_pending_automated_transfer = $site->has_pending_automated_transfer();
803
804 if ( $has_pending_automated_transfer ) {
805 $options[ $key ] = true;
806 }
807
808 break;
809 case 'woocommerce_is_active':
810 $options[ $key ] = $site->woocommerce_is_active();
811 break;
812 case 'editing_toolkit_is_active':
813 $options[ $key ] = $site->editing_toolkit_is_active();
814 break;
815 case 'design_type':
816 $options[ $key ] = $site->get_design_type();
817 break;
818 case 'site_segment':
819 $options[ $key ] = $site->get_site_segment();
820 break;
821 case 'import_engine':
822 $options[ $key ] = $site->get_import_engine();
823 break;
824 case 'is_pending_plan':
825 // D40032-code only added this function to the wpcom SAL subclasses. Absent any better idea,
826 // we'll just omit the key entirely in Jetpack.
827 if ( is_callable( array( $site, 'is_pending_plan' ) ) ) {
828 $options[ $key ] = $site->is_pending_plan();
829 }
830 break;
831
832 case 'is_wpforteams_site':
833 $options[ $key ] = $site->is_wpforteams_site();
834 break;
835 case 'p2_hub_blog_id':
836 $options[ $key ] = $site->get_p2_hub_blog_id();
837 break;
838
839 case 'site_creation_flow':
840 $site_creation_flow = $site->get_site_creation_flow();
841 if ( $site_creation_flow ) {
842 $options[ $key ] = $site_creation_flow;
843 }
844 break;
845 case 'site_source_slug':
846 $site_source_slug = $site->get_site_source_slug();
847 if ( $site_source_slug ) {
848 $options[ $key ] = $site_source_slug;
849 }
850 break;
851 case 'is_cloud_eligible':
852 $options[ $key ] = $site->is_cloud_eligible();
853 break;
854 case 'selected_features':
855 $selected_features = $site->get_selected_features();
856 if ( $selected_features ) {
857 $options[ $key ] = $selected_features;
858 }
859 break;
860 case 'anchor_podcast':
861 $options[ $key ] = $site->get_anchor_podcast();
862 break;
863 case 'was_created_with_blank_canvas_design':
864 $options[ $key ] = $site->was_created_with_blank_canvas_design();
865 break;
866 case 'videopress_storage_used':
867 $options[ $key ] = $this->site->get_videopress_storage_used();
868 break;
869 case 'is_difm_lite_in_progress':
870 $options[ $key ] = $site->is_difm_lite_in_progress();
871 break;
872 case 'difm_lite_site_options':
873 $difm_lite_options = $site->get_difm_lite_site_options();
874 $visible_options = self::$displayed_difm_lite_site_options;
875 $filtered_difm_lite_options = new stdClass();
876 if ( $difm_lite_options ) {
877 $filtered_difm_lite_options = array_filter(
878 $difm_lite_options,
879 function ( $key ) use ( $visible_options ) {
880 return in_array( $key, $visible_options, true );
881 },
882 ARRAY_FILTER_USE_KEY
883 );
884 }
885 $options[ $key ] = $filtered_difm_lite_options;
886 break;
887 case 'site_intent':
888 $options[ $key ] = $site->get_site_intent();
889 break;
890 case 'site_vertical_id':
891 $options[ $key ] = $site->get_site_vertical_id();
892 break;
893 case 'blogging_prompts_settings':
894 if ( current_user_can( 'edit_posts' ) ) {
895 $options[ $key ] = $site->get_blogging_prompts_settings( get_current_user_id(), $site->blog_id );
896 }
897 break;
898 case 'launchpad_screen':
899 $options[ $key ] = $site->get_launchpad_screen();
900 break;
901 case 'launchpad_checklist_tasks_statuses':
902 $options[ $key ] = $site->get_launchpad_checklist_tasks_statuses();
903 break;
904 case 'wpcom_production_blog_id':
905 $options[ $key ] = $site->get_wpcom_production_blog_id();
906 break;
907 case 'wpcom_staging_blog_ids':
908 $options[ $key ] = $site->get_wpcom_staging_blog_ids();
909 break;
910 case 'can_blaze':
911 $options[ $key ] = $site->can_blaze();
912 break;
913 case 'wpcom_site_setup':
914 $options[ $key ] = $site->get_wpcom_site_setup();
915 break;
916 case 'is_commercial':
917 $options[ $key ] = $site->is_commercial();
918 break;
919 case 'wpcom_admin_interface':
920 $options[ $key ] = $site->get_wpcom_admin_interface();
921 break;
922 }
923 }
924
925 return $options;
926 }
927
928 /**
929 * Build meta response.
930 *
931 * @param array $response - the response.
932 */
933 protected function build_meta_response( &$response ) {
934 $links = array(
935 'self' => (string) $this->links->get_site_link( $this->site->blog_id ),
936 'help' => (string) $this->links->get_site_link( $this->site->blog_id, 'help' ),
937 'posts' => (string) $this->links->get_site_link( $this->site->blog_id, 'posts/' ),
938 'comments' => (string) $this->links->get_site_link( $this->site->blog_id, 'comments/' ),
939 'xmlrpc' => (string) $this->site->get_xmlrpc_url(),
940 );
941
942 $icon = $this->site->get_icon();
943 if ( ! empty( $icon ) && ! empty( $icon['media_id'] ) ) {
944 $links['site_icon'] = (string) $this->links->get_site_link( $this->site->blog_id, 'media/' . $icon['media_id'] );
945 }
946
947 $response['meta'] = (object) array(
948 'links' => (object) $links,
949 );
950 }
951
952 /**
953 * Apply any WPCOM-only response components to a Jetpack site response.
954 *
955 * @param array $response - the response.
956 */
957 public function decorate_jetpack_response( &$response ) {
958 $this->site = $this->get_platform()->get_site( $response->ID );
959 switch_to_blog( $this->site->get_id() );
960
961 $wpcom_response = $this->render_response_keys( self::$jetpack_response_field_additions );
962
963 foreach ( $wpcom_response as $key => $value ) {
964 $response->{ $key } = $value;
965 }
966
967 if ( $this->has_user_access() || $this->has_blog_access( $this->api->token_details ) ) {
968 $wpcom_member_response = $this->render_response_keys( self::$jetpack_response_field_member_additions );
969
970 foreach ( $wpcom_member_response as $key => $value ) {
971 $response->{ $key } = $value;
972 }
973 } else {
974 // ensure private data is not rendered for non members of the site.
975 unset( $response->options );
976 unset( $response->is_vip );
977 unset( $response->single_user_site );
978 unset( $response->is_private );
979 unset( $response->is_coming_soon );
980 unset( $response->capabilities );
981 unset( $response->lang );
982 unset( $response->user_can_manage );
983 unset( $response->is_multisite );
984 unset( $response->site_owner );
985 unset( $response->plan );
986 unset( $response->products );
987 unset( $response->zendesk_site_meta );
988 }
989
990 // render additional options.
991 if ( $response->options ) {
992 $wpcom_options_response = $this->render_option_keys( self::$jetpack_response_option_additions );
993
994 // Remove heic from jetpack (and atomic) sites so that the iOS app know to convert the file format into a JPEG.
995 // heic fromat is currently not supported by for uploading.
996 // See https://jetpackp2.wordpress.com/2020/08/19/image-uploads-in-the-wp-ios-app-broken
997 if ( $this->site->is_jetpack() && isset( $response->options['allowed_file_types'] ) ) {
998 $remove_file_types = array(
999 'heic',
1000 );
1001 $response->options['allowed_file_types'] = array_values( array_diff( $response->options['allowed_file_types'], $remove_file_types ) );
1002 }
1003
1004 foreach ( $wpcom_options_response as $key => $value ) {
1005 $response->options[ $key ] = $value;
1006 }
1007 }
1008
1009 restore_current_blog();
1010 return $response; // possibly no need since it's modified in place.
1011 }
1012 }
1013
1014 new WPCOM_JSON_API_List_Post_Formats_Endpoint(
1015 array(
1016 'description' => 'Get a list of post formats supported by a site.',
1017 'group' => '__do_not_document',
1018 'stat' => 'sites:X:post-formats',
1019
1020 'method' => 'GET',
1021 'path' => '/sites/%s/post-formats',
1022 'path_labels' => array(
1023 '$site' => '(int|string) Site ID or domain',
1024 ),
1025
1026 'query_parameters' => array(
1027 'context' => false,
1028 ),
1029
1030 'allow_fallback_to_jetpack_blog_token' => true,
1031
1032 'response_format' => array(
1033 'formats' => '(object) An object of supported post formats, each key a supported format slug mapped to its display string.',
1034 ),
1035 )
1036 );
1037
1038 /**
1039 * List Post Formates endpoint class.
1040 */
1041 class WPCOM_JSON_API_List_Post_Formats_Endpoint extends WPCOM_JSON_API_Endpoint { // phpcs:ignore
1042 /**
1043 *
1044 * API callback.
1045 *
1046 * /sites/%s/post-formats -> $blog_id
1047 *
1048 * @param string $path - the path.
1049 * @param int $blog_id - the blog ID.
1050 */
1051 public function callback( $path = '', $blog_id = 0 ) {
1052 $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
1053 if ( is_wp_error( $blog_id ) ) {
1054 return $blog_id;
1055 }
1056
1057 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1058 $this->load_theme_functions();
1059 }
1060
1061 // Get a list of supported post formats.
1062 $all_formats = get_post_format_strings();
1063 $supported = get_theme_support( 'post-formats' );
1064
1065 $response = array(
1066 'formats' => array(),
1067 );
1068 $supported_formats = $response['formats'];
1069
1070 if ( isset( $supported[0] ) ) {
1071 foreach ( $supported[0] as $format ) {
1072 $supported_formats[ $format ] = $all_formats[ $format ];
1073 }
1074 }
1075
1076 $response['formats'] = (object) $supported_formats;
1077
1078 return $response;
1079 }
1080 }
1081
1082 new WPCOM_JSON_API_List_Page_Templates_Endpoint(
1083 array(
1084 'description' => 'Get a list of page templates supported by a site.',
1085 'group' => 'sites',
1086 'stat' => 'sites:X:post-templates',
1087
1088 'method' => 'GET',
1089 'path' => '/sites/%s/page-templates',
1090 'path_labels' => array(
1091 '$site' => '(int|string) Site ID or domain',
1092 ),
1093 'query_parameters' => array(
1094 'context' => false,
1095 ),
1096 'response_format' => array(
1097 'templates' => '(array) A list of supported page templates. Contains label and file.',
1098 ),
1099 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/33534099/page-templates',
1100 )
1101 );
1102
1103 /**
1104 * List page templates endpoint class.
1105 */
1106 class WPCOM_JSON_API_List_Page_Templates_Endpoint extends WPCOM_JSON_API_Endpoint { // phpcs:ignore
1107 /**
1108 *
1109 * API callback.
1110 * /sites/%s/page-templates -> $blog_id
1111 *
1112 * @param string $path - the path.
1113 * @param int $blog_id - the blog ID.
1114 */
1115 public function callback( $path = '', $blog_id = 0 ) {
1116 $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
1117 if ( is_wp_error( $blog_id ) ) {
1118 return $blog_id;
1119 }
1120
1121 if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
1122 $this->load_theme_functions();
1123 }
1124
1125 $response = array();
1126 $page_templates = array();
1127
1128 $templates = get_page_templates();
1129 ksort( $templates );
1130
1131 foreach ( array_keys( $templates ) as $label ) {
1132 $page_templates[] = array(
1133 'label' => $label,
1134 'file' => $templates[ $label ],
1135 );
1136 }
1137
1138 $response['templates'] = $page_templates;
1139
1140 return $response;
1141 }
1142 }
1143