Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
61.34% covered (warning)
61.34%
219 / 357
71.30% covered (warning)
71.30%
82 / 115
CRAP
0.00% covered (danger)
0.00%
0 / 1
SAL_Site
62.04% covered (warning)
62.04%
219 / 353
71.30% covered (warning)
71.30%
82 / 115
2527.26
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 get_id
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_slug
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_name
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 get_description
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 get_url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_post_count
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_quota
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_blogging_prompts_settings
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 has_videopress
n/a
0 / 0
n/a
0 / 0
0
 get_videopress_storage_used
n/a
0 / 0
n/a
0 / 0
0
 upgraded_filetypes_enabled
n/a
0 / 0
n/a
0 / 0
0
 is_mapped_domain
n/a
0 / 0
n/a
0 / 0
0
 get_unmapped_url
n/a
0 / 0
n/a
0 / 0
0
 is_redirect
n/a
0 / 0
n/a
0 / 0
0
 is_headstart_fresh
n/a
0 / 0
n/a
0 / 0
0
 featured_images_enabled
n/a
0 / 0
n/a
0 / 0
0
 has_wordads
n/a
0 / 0
n/a
0 / 0
0
 get_frame_nonce
n/a
0 / 0
n/a
0 / 0
0
 get_jetpack_frame_nonce
n/a
0 / 0
n/a
0 / 0
0
 allowed_file_types
n/a
0 / 0
n/a
0 / 0
0
 get_post_formats
n/a
0 / 0
n/a
0 / 0
0
 is_private
n/a
0 / 0
n/a
0 / 0
0
 is_coming_soon
n/a
0 / 0
n/a
0 / 0
0
 is_following
n/a
0 / 0
n/a
0 / 0
0
 get_subscribers_count
n/a
0 / 0
n/a
0 / 0
0
 get_locale
n/a
0 / 0
n/a
0 / 0
0
 is_jetpack
n/a
0 / 0
n/a
0 / 0
0
 is_jetpack_connection
n/a
0 / 0
n/a
0 / 0
0
 get_jetpack_modules
n/a
0 / 0
n/a
0 / 0
0
 is_module_active
n/a
0 / 0
n/a
0 / 0
0
 is_vip
n/a
0 / 0
n/a
0 / 0
0
 is_multisite
n/a
0 / 0
n/a
0 / 0
0
 get_site_owner
n/a
0 / 0
n/a
0 / 0
0
 is_single_user_site
n/a
0 / 0
n/a
0 / 0
0
 get_plan
n/a
0 / 0
n/a
0 / 0
0
 get_ak_vp_bundle_enabled
n/a
0 / 0
n/a
0 / 0
0
 get_podcasting_archive
n/a
0 / 0
n/a
0 / 0
0
 get_import_engine
n/a
0 / 0
n/a
0 / 0
0
 get_jetpack_seo_front_page_description
n/a
0 / 0
n/a
0 / 0
0
 get_jetpack_seo_title_formats
n/a
0 / 0
n/a
0 / 0
0
 get_verification_services_codes
n/a
0 / 0
n/a
0 / 0
0
 before_render
n/a
0 / 0
n/a
0 / 0
0
 after_render
n/a
0 / 0
n/a
0 / 0
0
 after_render_options
n/a
0 / 0
n/a
0 / 0
0
 wrap_post
n/a
0 / 0
n/a
0 / 0
0
 is_a8c_publication
n/a
0 / 0
n/a
0 / 0
0
 get_user_interactions
n/a
0 / 0
n/a
0 / 0
0
 is_deleted
n/a
0 / 0
n/a
0 / 0
0
 is_a4a_client
n/a
0 / 0
n/a
0 / 0
0
 is_a4a_dev_site
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 current_user_can
n/a
0 / 0
n/a
0 / 0
0
 is_automated_transfer
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 is_wpforteams_site
n/a
0 / 0
n/a
0 / 0
0
 get_p2_hub_blog_id
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_p2_organization_id
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_p2_thumbnail_elements
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_wpcom_atomic
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_wpcom_store
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 was_trial
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 was_upgraded_from_trial
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 woocommerce_is_active
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 editing_toolkit_is_active
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_cloud_eligible
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_products
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_post_by_id
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 validate_access
60.00% covered (warning)
60.00%
9 / 15
0.00% covered (danger)
0.00%
0 / 1
12.10
 current_user_can_access_post_type
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
42
 get_post_type_object
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_post_type_allowed
53.85% covered (warning)
53.85%
7 / 13
0.00% covered (danger)
0.00%
0 / 1
11.82
 get_whitelisted_post_types
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 user_can_view_post
53.49% covered (warning)
53.49%
23 / 43
0.00% covered (danger)
0.00%
0 / 1
60.25
 get_post_id_by_name
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
42
 get_post_by_name
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 user_can_manage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_xmlrpc_url
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 get_registered_date
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 get_last_update_date
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 get_capabilities
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
2
 is_visible
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
3.14
 get_logo
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
2.03
 get_timezone
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_gmt_offset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_login_url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_admin_url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_theme_slug
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_theme_errors
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 get_header_image
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_background_color
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_image_default_link_type
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_image_thumbnail_width
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_image_thumbnail_height
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_image_thumbnail_crop
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_image_medium_width
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_image_medium_height
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_image_large_width
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_image_large_height
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_permalink_structure
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_default_post_format
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_default_category
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_show_on_front
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_custom_front_page
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_default_likes_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_default_sharing_status
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 get_default_comment_status
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 default_ping_status
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_publicize_permanently_disabled
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 get_page_on_front
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_page_for_posts
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_headstart
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_wordpress_version
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_domain_only
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 get_blog_public
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 has_pending_automated_transfer
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 signup_is_store
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_roles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_design_type
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 get_site_goals
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 get_launch_status
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_migration_meta
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_site_segment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_site_vertical_id
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_site_creation_flow
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_site_source_slug
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_selected_features
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 was_created_with_blank_canvas_design
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_anchor_podcast
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_difm_lite_in_progress
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 get_difm_lite_site_options
28.57% covered (danger)
28.57%
4 / 14
0.00% covered (danger)
0.00%
0 / 1
24.86
 is_gating_business_q1
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 is_legacy_gating_site
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 get_site_intent
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_site_partner_bundle
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_launchpad_screen
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_onboarding_segment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_launchpad_checklist_tasks_statuses
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 is_ai_launchpad_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 is_ai_launchpad_dismissed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_ai_launchpad_completed
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_ai_launchpad_no_guidance
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 is_past_no_guidance_cutoff
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 get_migration_source_site_domain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_wpcom_staging_site
n/a
0 / 0
n/a
0 / 0
0
 get_wpcom_production_blog_id
n/a
0 / 0
n/a
0 / 0
0
 get_wpcom_staging_blog_ids
n/a
0 / 0
n/a
0 / 0
0
 can_blaze
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_wpcom_site_setup
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_commercial
77.78% covered (warning)
77.78%
7 / 9
0.00% covered (danger)
0.00%
0 / 1
5.27
 get_is_commercial_reasons
66.67% covered (warning)
66.67%
4 / 6
0.00% covered (danger)
0.00%
0 / 1
4.59
 get_wpcom_admin_interface
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_wpcom_classic_early_release
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_apm_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_zendesk_site_meta
n/a
0 / 0
n/a
0 / 0
0
 is_pending_plan
n/a
0 / 0
n/a
0 / 0
0
 is_garden
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 garden_name
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 garden_partner
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 garden_is_provisioned
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_wpcom_flex
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 is_big_sky_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_atomic_email_block
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_jetpack_recovery_mode_status
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 get_jetpack_sso_require_two_step
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2/**
3 * This file defines the base class for the Site Abstraction Layer (SAL).
4 * Note that this is the site "as seen by user $user_id with token $token", which
5 * is why we pass the token to the platform; these site instances are value objects
6 * to be used in the context of a single request for a single user.
7 * Also note that at present this class _assumes_ you've "switched to"
8 * the site in question, and functions like `get_bloginfo( 'name' )` will
9 * therefore return the correct value.
10 *
11 * @package automattic/jetpack
12 **/
13
14use Automattic\Jetpack\Blaze;
15use Automattic\Jetpack\Constants;
16use Automattic\Jetpack\Status;
17use Automattic\Jetpack\Status\Host;
18
19if ( ! defined( 'ABSPATH' ) ) {
20    exit( 0 );
21}
22
23require_once __DIR__ . '/class.json-api-date.php';
24require_once __DIR__ . '/class.json-api-post-base.php';
25
26/**
27 * Base class for SAL_Site.
28 * The abstract functions here are extended by Abstract_Jetpack_Site in class.json-api-site-jetpack-base.php.
29 */
30abstract class SAL_Site {
31
32    /**
33     *  The Jetpack blog ID for the site.
34     *
35     * @var int
36     */
37    public $blog_id;
38
39    /**
40     * A new WPORG_Platform instance.
41     *
42     * @see class.json-api-platform-jetpack.php.
43     *
44     * @var WPORG_Platform
45     */
46    public $platform;
47
48    /**
49     * Contructs the SAL_Site instance.
50     *
51     * @param int            $blog_id The Jetpack blog ID for the site.
52     * @param WPORG_Platform $platform  A new WPORG_Platform instance.
53     */
54    public function __construct( $blog_id, $platform ) {
55        $this->blog_id  = $blog_id;
56        $this->platform = $platform;
57    }
58
59    /**
60     * Get the blog_id property.
61     *
62     * @return int
63     */
64    public function get_id() {
65        return $this->blog_id;
66    }
67
68    /**
69     * Returns the site slug.
70     *
71     * @return string
72     */
73    public function get_slug() {
74        return ( new Status() )->get_site_suffix();
75    }
76
77    /**
78     * Returns the site name.
79     *
80     * @return string
81     */
82    public function get_name() {
83        $name = get_bloginfo( 'name' );
84        return is_string( $name ) ? htmlspecialchars_decode( $name, ENT_QUOTES ) : '';
85    }
86
87    /**
88     * Returns the site description.
89     *
90     * @return string
91     */
92    public function get_description() {
93        $description = get_bloginfo( 'description' );
94        return is_string( $description ) ? htmlspecialchars_decode( $description, ENT_QUOTES ) : '';
95    }
96
97    /**
98     * Returns the URL for the current site.
99     *
100     * @return string
101     */
102    public function get_url() {
103        return (string) home_url();
104    }
105
106    /**
107     * Returns the number of published posts with the 'post' post-type.
108     *
109     * @return int
110     */
111    public function get_post_count() {
112        return (int) wp_count_posts( 'post' )->publish;
113    }
114
115    /**
116     * A prototype function for get_quota - currently returns null.
117     *
118     * @return null
119     */
120    public function get_quota() {
121        return null;
122    }
123
124    /**
125     * Returns an array of blogging prompt settings. Only applicable on WordPress.com.
126     *
127     * Data comes from .com since the fearture requires a .com connection to work.
128     *
129     * @param int $user_id the current user_id.
130     * @param int $blog_id the blog id in this context.
131     */
132    public function get_blogging_prompts_settings( $user_id, $blog_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
133        return false;
134    }
135
136    /**
137     * Returns true if a site has the 'videopress' option enabled, false otherwise.
138     *
139     * @see class.json-api-site-jetpack.php for implementation.
140     */
141    abstract public function has_videopress();
142
143    /**
144     * Returns VideoPress storage used, in MB.
145     *
146     * @see class.json-api-site-jetpack-shadow.php on WordPress.com for implementation. Only applicable on WordPress.com.
147     */
148    abstract public function get_videopress_storage_used();
149
150    /**
151     * Sets the upgraded_filetypes_enabled Jetpack option to true as a default. Only relevant for WordPress.com sites.
152     *
153     * @see class.json-api-site-jetpack.php for implementation.
154     */
155    abstract public function upgraded_filetypes_enabled();
156
157    /**
158     * Sets the is_mapped_domain Jetpack option to true as a default.
159     *
160     * Primarily used in WordPress.com to confirm the current blog's domain does or doesn't match the primary redirect.
161     *
162     * @see class.json-api-site-jetpack.php for implementation.
163     */
164    abstract public function is_mapped_domain();
165
166    /**
167     * Fallback to the home URL since all Jetpack sites don't have an unmapped *.wordpress.com domain.
168     *
169     * @see class.json-api-site-jetpack.php for implementation.
170     */
171    abstract public function get_unmapped_url();
172
173    /**
174     * Whether the domain is a site redirect or not. Defaults to false on a Jetpack site.
175     *
176     * Primarily used in WordPress.com where it is determined if a HTTP status check is a redirect or not and whether an exception should be thrown.
177     *
178     * @see class.json-api-site-jetpack.php for implementation.
179     */
180    abstract public function is_redirect();
181
182    /**
183     * Defaults to false on Jetpack sites, however is used on WordPress.com sites, where it returns true if the headstart-fresh blog sticker is present.
184     *
185     * @see class.json-api-site-jetpack.php for implementation.
186     */
187    abstract public function is_headstart_fresh();
188
189    /**
190     * If the site's current theme supports post thumbnails, return true (otherwise return false).
191     *
192     * @see class.json-api-site-jetpack-base.php for implementation.
193     */
194    abstract public function featured_images_enabled();
195
196    /**
197     * Whether or not the Jetpack 'wordads' module is active on the site.
198     *
199     * @see class.json-api-site-jetpack.php for implementation.
200     */
201    abstract public function has_wordads();
202
203    /**
204     * Defaults to false on Jetpack sites, however is used on WordPress.com sites. This nonce is used for previews on Jetpack sites.
205     *
206     * @see class.json-api-site-jetpack.php for implementation.
207     */
208    abstract public function get_frame_nonce();
209
210    /**
211     * Defaults to false on Jetpack sites, however is used on WordPress.com sites where
212     * it creates a nonce to be used with iframed block editor requests to a Jetpack site.
213     *
214     * @see class.json-api-site-jetpack.php for implementation.
215     */
216    abstract public function get_jetpack_frame_nonce();
217
218    /**
219     * Returns the allowed mime types and file extensions for a site.
220     *
221     * @see class.json-api-site-jetpack.php for implementation.
222     */
223    abstract public function allowed_file_types();
224
225    /**
226     * Returns an array of supported post formats.
227     *
228     * @see class.json-api-site-jetpack-base.php for implementation.
229     */
230    abstract public function get_post_formats();
231
232    /**
233     * Return site's privacy status.
234     *
235     * @see class.json-api-site-jetpack.php for implementation.
236     */
237    abstract public function is_private();
238
239    /**
240     * Return site's coming soon status.
241     *
242     * @see class.json-api-site-jetpack.php for implementation.
243     */
244    abstract public function is_coming_soon();
245
246    /**
247     * Whether or not the current user is following this blog. Defaults to false.
248     *
249     * @see class.json-api-site-jetpack.php for implementation.
250     */
251    abstract public function is_following();
252
253    /**
254     * Defaults to 0 for the number of WordPress.com subscribers - this is filled in on the WordPress.com side.
255     *
256     * @see class.json-api-site-jetpack.php for implementation.
257     */
258    abstract public function get_subscribers_count();
259
260    /**
261     * Returns the language code for the current site.
262     *
263     * @see class.json-api-site-jetpack.php for implementation.
264     */
265    abstract public function get_locale();
266
267    /**
268     * The flag indicates that the site has Jetpack installed.
269     *
270     * @see class.json-api-site-jetpack.php for implementation.
271     */
272    abstract public function is_jetpack();
273
274    /**
275     * The flag indicates that the site is connected to WP.com via Jetpack Connection.
276     *
277     * @see class.json-api-site-jetpack.php for implementation.
278     */
279    abstract public function is_jetpack_connection();
280
281    /**
282     * This function returns the values of any active Jetpack modules.
283     *
284     * @see class.json-api-site-jetpack-base.php for implementation.
285     */
286    abstract public function get_jetpack_modules();
287
288    /**
289     * This function returns true if a specified Jetpack module is active, false otherwise.
290     *
291     * @see class.json-api-site-jetpack-base.php for implementation.
292     *
293     * @param string $module The Jetpack module name to check.
294     */
295    abstract public function is_module_active( $module );
296
297    /**
298     * This function returns false for a check as to whether a site is a VIP site or not.
299     *
300     * @see class.json-api-site-jetpack-base.php for implementation.
301     */
302    abstract public function is_vip();
303
304    /**
305     * Returns true if Multisite is enabled, false otherwise.
306     *
307     * @see class.json-api-site-jetpack.php for implementation.
308     */
309    abstract public function is_multisite();
310
311    /**
312     * Points to the user ID of the site owner
313     *
314     * @see class.json-api-site-jetpack.php for implementation.
315     */
316    abstract public function get_site_owner();
317
318    /**
319     * Returns true if the current site is a single user site, false otherwise.
320     *
321     * @see class.json-api-site-jetpack.php for implementation.
322     */
323    abstract public function is_single_user_site();
324
325    /**
326     * Defaults to false instead of returning the current site plan.
327     *
328     * @see class.json-api-site-jetpack.php for implementation.
329     */
330    abstract public function get_plan();
331
332    /**
333     * Empty function declaration - this function is filled out on the WordPress.com side, returning true if the site has an AK / VP bundle.
334     *
335     * @see class.json-api-site-jetpack.php and /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
336     */
337    abstract public function get_ak_vp_bundle_enabled();
338
339    /**
340     * Returns null for Jetpack sites. For WordPress.com sites this returns the value of the 'podcasting_archive' option.
341     *
342     * @see class.json-api-site-jetpack.php for implementation.
343     */
344    abstract public function get_podcasting_archive();
345
346    /**
347     * Return the last engine used for an import on the site. Not used in Jetpack.
348     *
349     * @see class.json-api-site-jetpack.php for implementation.
350     */
351    abstract public function get_import_engine();
352
353    /**
354     * Returns the front page meta description for current site.
355     *
356     * @see class.json-api-site-jetpack.php for implementation.
357     */
358    abstract public function get_jetpack_seo_front_page_description();
359
360    /**
361     * Returns custom title formats from site option.
362     *
363     * @see class.json-api-site-jetpack.php for implementation.
364     */
365    abstract public function get_jetpack_seo_title_formats();
366
367    /**
368     * Returns website verification codes. Allowed keys include: google, pinterest, bing, yandex, facebook.
369     *
370     * @see class.json-api-site-jetpack.php for implementation.
371     */
372    abstract public function get_verification_services_codes();
373
374    /**
375     * This function is implemented on WPCom sites, where a filter is removed which forces the URL to http.
376     *
377     * @see class.json-api-site-jetpack-base.php and /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
378     */
379    abstract public function before_render();
380
381    /**
382     * If a user has manage options permissions and the site is the main site of the network, make updates visible.
383     *
384     * Called after response_keys have been rendered, which itself is used to return all the necessary information for a site’s response.
385     *
386     * @see class.json-api-site-jetpack-base.php for implementation.
387     *
388     * @param array $response an array of the response keys.
389     */
390    abstract public function after_render( &$response );
391
392    /**
393     * Extends the Jetpack options array with details including site constraints, WordPress and Jetpack versions, and plugins using the Jetpack connection.
394     *
395     * @see class.json-api-site-jetpack-base.php for implementation.
396     * @todo factor this out? Seems an odd thing to have on a site
397     *
398     * @param array $options an array of the Jetpack options.
399     */
400    abstract public function after_render_options( &$options );
401
402    /**
403     * Wrap a WP_Post object with SAL methods, returning a Jetpack_Post object.
404     *
405     * @see class.json-api-site-jetpack.php for implementation.
406     *
407     * @param WP_Post $post A WP_Post object.
408     * @param string  $context The post request context (for example 'edit' or 'display').
409     */
410    abstract public function wrap_post( $post, $context );
411
412    /**
413     * For Jetpack sites this will always return false.
414     *
415     * @see class.json-api-site-jetpack-base.php for implementation.
416     *
417     * @param int $post_id The post id.
418     */
419    abstract protected function is_a8c_publication( $post_id );
420
421    /**
422     * Return the user interactions with a site. Not used in Jetpack.
423     *
424     * @see class.json-api-site-jetpack.php for implementation.
425     */
426    abstract public function get_user_interactions();
427
428    /**
429     * Flag a site as deleted. Not used in Jetpack.
430     *
431     * @see class.json-api-site-jetpack.php for implementation.
432     */
433    abstract public function is_deleted();
434
435    /**
436     * Indicates that a site is an A4A client. Not used in Jetpack.
437     *
438     * @see class.json-api-site-jetpack.php for implementation.
439     */
440    abstract public function is_a4a_client();
441
442    /**
443     * Indicates that a site is an A4A dev site.
444     *
445     * @return bool
446     */
447    public function is_a4a_dev_site() {
448        if ( function_exists( 'has_blog_sticker' ) ) {
449            return has_blog_sticker( 'a4a-is-dev-site' );
450        }
451        return false;
452    }
453
454    /**
455     * Return the user interactions with a site. Not used in Jetpack.
456     *
457     * @param string $role The capability to check.
458     * @return bool
459     * @see class.json-api-site-jetpack.php for implementation.
460     * @see class.json-api-site-wpcom.php (on WPCOM) for Simple-site implementation.
461     * @see class.json-api-site-jetpack-shadow.php (on WPCOM) for Atomic-site implementation.
462     */
463    abstract public function current_user_can( $role );
464
465    /**
466     * Defines a filter to set whether a site is an automated_transfer site or not.
467     *
468     * Default is false.
469     *
470     * @return bool
471     */
472    public function is_automated_transfer() {
473        /**
474         * Filter if a site is an automated-transfer site.
475         *
476         * @module json-api
477         *
478         * @since 6.4.0
479         *
480         * @param bool is_automated_transfer( $this->blog_id )
481         * @param int  $blog_id Blog identifier.
482         */
483        return apply_filters(
484            'jetpack_site_automated_transfer',
485            false,
486            $this->blog_id
487        );
488    }
489
490    /**
491     * Defaulting to false and not relevant for Jetpack sites, this is expanded on the WordPress.com side for a specific wp.com/start 'WP for teams' flow.
492     *
493     * @see class.json-api-site-jetpack.php for implementation.
494     */
495    abstract public function is_wpforteams_site();
496
497    /**
498     * Get hub blog id for P2 sites.
499     *
500     * @return null
501     */
502    public function get_p2_hub_blog_id() {
503        return null;
504    }
505
506    /**
507     * Getter for the p2 organization ID.
508     *
509     * @return int
510     */
511    public function get_p2_organization_id() {
512        return 0; // WPForTeams\Constants\NO_ORG_ID not loaded.
513    }
514
515    /**
516     * Get details used to render a thumbnail of the site. P2020 themed sites only.
517     *
518     * @return ?array
519     */
520    public function get_p2_thumbnail_elements() {
521        return null;
522    }
523
524    /**
525     * Detect whether a site is a WordPress.com on Atomic site.
526     *
527     * @return bool
528     */
529    public function is_wpcom_atomic() {
530        return ( new Host() )->is_woa_site();
531    }
532
533    /**
534     * Detect whether a site is an automated transfer site and WooCommerce is active.
535     *
536     * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
537     *
538     * @return bool - False for Jetpack-connected sites.
539     */
540    public function is_wpcom_store() {
541        return false;
542    }
543
544    /**
545     * Indicate whether this site was ever a specific trial.
546     *
547     * @param string $trial The trial type to check for.
548     *
549     * @return bool
550     */
551    public function was_trial( $trial ) {
552        if ( function_exists( 'has_blog_sticker' ) ) {
553            return has_blog_sticker( "had-{$trial}-trial" );
554        }
555        return false;
556    }
557
558    /**
559     * Indicate whether this site was upgraded from a trial plan at some point.
560     *
561     * @return bool
562     */
563    public function was_upgraded_from_trial() {
564        if ( function_exists( 'has_blog_sticker' ) ) {
565            return has_blog_sticker( 'has-upgraded-from-ecommerce-trial' );
566        }
567        return false;
568    }
569
570    /**
571     * Detect whether a site has the WooCommerce plugin active.
572     *
573     * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
574     *
575     * @return bool - Default false for Jetpack-connected sites.
576     */
577    public function woocommerce_is_active() {
578        return false;
579    }
580
581    /**
582     * Whether the Editing Toolkit plugin is active (relevant only on WordPress.com).
583     *
584     * @return true
585     */
586    public function editing_toolkit_is_active() {
587        return true;
588    }
589
590    /**
591     * Detect whether a site has access to the Jetpack cloud.
592     *
593     * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
594     *
595     * @return bool - Default false for Jetpack-connected sites.
596     */
597    public function is_cloud_eligible() {
598        return false;
599    }
600
601    /**
602     * Returns an array of WPCOM_Store products.
603     *
604     * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php.
605     *
606     * @return bool - Default empty array for Jetpack-connected sites.
607     */
608    public function get_products() {
609        return array();
610    }
611
612    /**
613     * Get post by ID
614     *
615     * @param int    $post_id The ID of the post.
616     * @param string $context The context by which the post data is required (display or edit).
617     *
618     * @return Jetpack_Post Post object on success, WP_Error object on failure
619     **/
620    public function get_post_by_id( $post_id, $context ) {
621        $post = get_post( $post_id, OBJECT, $context );
622
623        if ( ! $post ) {
624            return new WP_Error( 'unknown_post', 'Unknown post', 404 );
625        }
626
627        $wrapped_post = $this->wrap_post( $post, $context );
628        // validate access
629        return $this->validate_access( $wrapped_post );
630    }
631
632    /**
633     * Validate current user can access the post
634     *
635     * @param Jetpack_Post $post Post object.
636     *
637     * @return WP_Error|Jetpack_Post
638     */
639    private function validate_access( $post ) {
640        $context = $post->context;
641
642        if (
643            ! $this->is_post_type_allowed( $post->post_type )
644            && ! $this->is_a8c_publication( $post->ID )
645        ) {
646            return new WP_Error( 'unknown_post', 'Unknown post', 404 );
647        }
648
649        switch ( $context ) {
650            case 'edit':
651                if ( ! current_user_can( 'edit_post', $post->ID ) ) {
652                    return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
653                }
654                break;
655            case 'display':
656                $can_view = $this->user_can_view_post( $post );
657                if ( is_wp_error( $can_view ) ) {
658                    return $can_view;
659                }
660                break;
661            default:
662                return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 );
663        }
664
665        return $post;
666    }
667
668    /**
669     * Validate whether the current user can access the specified post type.
670     *
671     * @param string $post_type The post type to check.
672     * @param string $context The context by which the post data is required (display or edit).
673     *
674     * @return bool
675     */
676    public function current_user_can_access_post_type( $post_type, $context ) {
677        $post_type_object = $this->get_post_type_object( $post_type );
678        if ( ! $post_type_object ) {
679            return false;
680        }
681
682        switch ( $context ) {
683            case 'edit':
684                return current_user_can( $post_type_object->cap->edit_posts );
685            case 'display':
686                return $post_type_object->public || current_user_can( $post_type_object->cap->read_private_posts );
687            default:
688                return false;
689        }
690    }
691
692    /**
693     * Retrieves a post type object by name.
694     *
695     * @param string $post_type The post type to check.
696     *
697     * @return WP_Post_Type|null
698     */
699    protected function get_post_type_object( $post_type ) {
700        return get_post_type_object( $post_type );
701    }
702
703    /**
704     * Is the post type allowed?
705     *
706     * Function copied from class.json-api-endpoints.php.
707     *
708     * @param string $post_type Post type.
709     *
710     * @return bool
711     */
712    public function is_post_type_allowed( $post_type ) {
713        // if the post type is empty, that's fine, WordPress will default to post
714        if ( empty( $post_type ) ) {
715            return true;
716        }
717
718        // allow special 'any' type
719        if ( 'any' === $post_type ) {
720            return true;
721        }
722
723        // check for allowed types
724        if ( in_array( $post_type, $this->get_whitelisted_post_types(), true ) ) {
725            return true;
726        }
727
728        $post_type_object = get_post_type_object( $post_type );
729        if ( $post_type_object ) {
730            if ( ! empty( $post_type_object->show_in_rest ) ) {
731                return $post_type_object->show_in_rest;
732            }
733            if ( ! empty( $post_type_object->publicly_queryable ) ) {
734                return $post_type_object->publicly_queryable;
735            }
736        }
737
738        return ! empty( $post_type_object->public );
739    }
740
741    /**
742     * Gets the whitelisted post types that JP should allow access to.
743     *
744     * Function copied from class.json-api-endpoints.php.
745     *
746     * @return array Whitelisted post types.
747     */
748    public function get_whitelisted_post_types() {
749        $allowed_types = array( 'post', 'page', 'revision' );
750
751        /**
752         * Filter the post types Jetpack has access to, and can synchronize with WordPress.com.
753         *
754         * @module json-api
755         *
756         * @since 2.2.3
757         *
758         * @param array $allowed_types Array of whitelisted post types. Default to `array( 'post', 'page', 'revision' )`.
759         */
760        $allowed_types = apply_filters( 'rest_api_allowed_post_types', $allowed_types );
761
762        return array_unique( $allowed_types );
763    }
764
765    /**
766     * Can the user view the post?
767     *
768     * Function copied from class.json-api-endpoints.php and modified.
769     *
770     * @param Jetpack_Post $post Post object.
771     * @return bool|WP_Error
772     */
773    private function user_can_view_post( $post ) {
774        if ( ! $post || is_wp_error( $post ) ) {
775            return false;
776        }
777        // If the post is of status inherit, check if the parent exists ( different to 0 ) to check for the parent status object.
778        if ( 'inherit' === $post->post_status && 0 !== (int) $post->post_parent ) {
779            $parent_post     = get_post( $post->post_parent );
780            $post_status_obj = $parent_post ? get_post_status_object( $parent_post->post_status ) : null;
781        } else {
782            $post_status_obj = get_post_status_object( $post->post_status );
783        }
784
785        $authorized = false;
786
787        if ( $post_status_obj ) {
788            $authorized = $post_status_obj->public
789                || ( is_user_logged_in() && (
790                ( $post_status_obj->protected && current_user_can( 'edit_post', $post->ID ) )
791                || ( $post_status_obj->private && current_user_can( 'read_post', $post->ID ) )
792                ) );
793        }
794
795        $authorized = $authorized || (
796            ( 'trash' === $post->post_status && current_user_can( 'edit_post', $post->ID ) )
797            || 'auto-draft' === $post->post_status
798        );
799
800        if ( ! $authorized ) {
801            return new WP_Error( 'unauthorized', 'User cannot view post', 403 );
802        }
803
804        if (
805            ( new Status() )->is_private_site() &&
806            /**
807             * Filter access to a specific post.
808             *
809             * @module json-api
810             *
811             * @since 3.4.0
812             *
813             * @param bool current_user_can( 'read_post', $post->ID ) Can the current user access the post.
814             * @param WP_Post $post Post data.
815             */
816            ! apply_filters(
817                'wpcom_json_api_user_can_view_post',
818                current_user_can( 'read_post', $post->ID ),
819                $post
820            )
821        ) {
822            return new WP_Error(
823                'unauthorized',
824                'User cannot view post',
825                array(
826                    'status_code' => 403,
827                    'error'       => 'private_blog',
828                )
829            );
830        }
831
832        if ( strlen( $post->post_password ) && ! current_user_can( 'edit_post', $post->ID ) ) {
833            return new WP_Error(
834                'unauthorized',
835                'User cannot view password protected post',
836                array(
837                    'status_code' => 403,
838                    'error'       => 'password_protected',
839                )
840            );
841        }
842
843        return true;
844    }
845
846    /**
847     * Get post ID by name
848     *
849     * Attempts to match name on post title and page path
850     *
851     * @param string $name The post name.
852     *
853     * @return int|WP_Error Post ID on success, WP_Error object on failure
854     */
855    public function get_post_id_by_name( $name ) {
856        $name = sanitize_title( $name );
857
858        if ( ! $name ) {
859            return new WP_Error( 'invalid_post', 'Invalid post', 400 );
860        }
861
862        $posts = get_posts(
863            array(
864                'name'        => $name,
865                'numberposts' => 1,
866                'post_type'   => $this->get_whitelisted_post_types(),
867            )
868        );
869
870        if ( ! $posts || ! isset( $posts[0]->ID ) || ! $posts[0]->ID ) {
871            $page = get_page_by_path( $name );
872
873            if ( ! $page ) {
874                return new WP_Error( 'unknown_post', 'Unknown post', 404 );
875            }
876
877            return $page->ID;
878        }
879
880        return (int) $posts[0]->ID;
881    }
882
883    /**
884     * Get post by name
885     *
886     * Attempts to match name on post title and page path
887     *
888     * @param string $name The post name.
889     * @param string $context (display or edit).
890     *
891     * @return Jetpack_Post|WP_Error Post object on success, WP_Error object on failure
892     **/
893    public function get_post_by_name( $name, $context ) {
894        $post_id = $this->get_post_id_by_name( $name );
895        if ( is_wp_error( $post_id ) ) {
896            return $post_id;
897        }
898
899        return $this->get_post_by_id( $post_id, $context );
900    }
901
902    /**
903     * Whether or not the current user is an admin (has option management capabilities).
904     *
905     * @return bool
906     **/
907    public function user_can_manage() {
908        return current_user_can( 'manage_options' );
909    }
910
911    /**
912     * Returns the XMLRPC URL - the site URL including the URL scheme that is used when querying your site's REST API endpoint.
913     *
914     * @return string
915     **/
916    public function get_xmlrpc_url() {
917        $xmlrpc_scheme = apply_filters( 'wpcom_json_api_xmlrpc_scheme', wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME ) );
918        return site_url( 'xmlrpc.php', $xmlrpc_scheme );
919    }
920
921    /**
922     * Returns a date/time string with the date the site was registered, or a default date/time string otherwise.
923     *
924     * @return string
925     **/
926    public function get_registered_date() {
927        if ( function_exists( 'get_blog_details' ) ) {
928            $blog_details = get_blog_details();
929            if ( ! empty( $blog_details->registered ) ) {
930                return WPCOM_JSON_API_Date::format_date( $blog_details->registered );
931            }
932        }
933
934        return '0000-00-00T00:00:00+00:00';
935    }
936
937    /**
938     * Returns a date/time string with the date the site was last updated, or a default date/time string otherwise.
939     *
940     * @return string
941     **/
942    public function get_last_update_date() {
943        if ( function_exists( 'get_blog_details' ) ) {
944            $blog_details = get_blog_details();
945            if ( ! empty( $blog_details->last_updated ) ) {
946                return WPCOM_JSON_API_Date::format_date( $blog_details->last_updated );
947            }
948        }
949
950        return '0000-00-00T00:00:00+00:00';
951    }
952
953    /**
954     * Returns an array including the current users relevant capabilities.
955     *
956     * @return array
957     **/
958    public function get_capabilities() {
959        $is_wpcom_blog_owner = wpcom_get_blog_owner() === (int) get_current_user_id();
960
961        return array(
962            'edit_pages'          => $this->current_user_can( 'edit_pages' ),
963            'edit_posts'          => $this->current_user_can( 'edit_posts' ),
964            'edit_others_posts'   => $this->current_user_can( 'edit_others_posts' ),
965            'edit_others_pages'   => $this->current_user_can( 'edit_others_pages' ),
966            'delete_posts'        => $this->current_user_can( 'delete_posts' ),
967            'delete_others_posts' => $this->current_user_can( 'delete_others_posts' ),
968            'edit_theme_options'  => $this->current_user_can( 'edit_theme_options' ),
969            'edit_users'          => $this->current_user_can( 'edit_users' ),
970            'list_users'          => $this->current_user_can( 'list_users' ),
971            'manage_categories'   => $this->current_user_can( 'manage_categories' ),
972            'manage_options'      => $this->current_user_can( 'manage_options' ),
973            'moderate_comments'   => $this->current_user_can( 'moderate_comments' ),
974            'activate_wordads'    => $is_wpcom_blog_owner,
975            'promote_users'       => $this->current_user_can( 'promote_users' ),
976            'publish_posts'       => $this->current_user_can( 'publish_posts' ),
977            'upload_files'        => $this->current_user_can( 'upload_files' ),
978            'delete_users'        => $this->current_user_can( 'delete_users' ),
979            'remove_users'        => $this->current_user_can( 'remove_users' ),
980            'own_site'            => $is_wpcom_blog_owner,
981            'view_stats'          => stats_is_blog_user( $this->blog_id ),
982            'activate_plugins'    => $this->current_user_can( 'activate_plugins' ),
983            'update_plugins'      => $this->current_user_can( 'update_plugins' ),
984            'export'              => $this->current_user_can( 'export' ),
985            'import'              => $this->current_user_can( 'import' ),
986        );
987    }
988
989    /**
990     * Whether or not a site is public.
991     *
992     * @return bool
993     **/
994    public function is_visible() {
995        if ( is_user_logged_in() ) {
996            $current_user = wp_get_current_user();
997            $visible      = (array) get_user_meta( $current_user->ID, 'blog_visibility', true );
998
999            $is_visible = true;
1000            if ( isset( $visible[ $this->blog_id ] ) ) {
1001                $is_visible = (bool) $visible[ $this->blog_id ];
1002            }
1003
1004            // null and true are visible
1005            return $is_visible;
1006        }
1007
1008        return null;
1009    }
1010
1011    /**
1012     * Creates and returns an array with logo settings.
1013     *
1014     * @return array
1015     **/
1016    public function get_logo() {
1017        // Set an empty response array.
1018        $logo_setting = array(
1019            'id'    => 0,
1020            'sizes' => array(),
1021            'url'   => '',
1022        );
1023
1024        // Get current site logo values.
1025        $logo_id = get_option( 'site_logo' );
1026
1027        // Update the response array if there's a site logo currenty active.
1028        if ( $logo_id ) {
1029            $logo_setting['id']  = $logo_id;
1030            $logo_setting['url'] = wp_get_attachment_url( $logo_id );
1031        }
1032
1033        return $logo_setting;
1034    }
1035
1036    /**
1037     * Returns the timezone string from the site's settings (eg. 'Europe/London').
1038     *
1039     * @return string
1040     **/
1041    public function get_timezone() {
1042        return (string) get_option( 'timezone_string' );
1043    }
1044
1045    /**
1046     * Returns the GMT offset from the site's settings (eg. 5.5).
1047     *
1048     * @return float
1049     **/
1050    public function get_gmt_offset() {
1051        return (float) get_option( 'gmt_offset' );
1052    }
1053
1054    /**
1055     * Returns the site's login URL.
1056     *
1057     * @return string
1058     **/
1059    public function get_login_url() {
1060        return wp_login_url();
1061    }
1062
1063    /**
1064     * Returns the URL for a site's admin area.
1065     *
1066     * @return string
1067     **/
1068    public function get_admin_url() {
1069        return get_admin_url();
1070    }
1071
1072    /**
1073     * Returns the theme's slug (eg. 'twentytwentytwo')
1074     *
1075     * @return string
1076     **/
1077    public function get_theme_slug() {
1078        return get_option( 'stylesheet' );
1079    }
1080
1081    /**
1082     * Returns a list of errors for broken themes on the site.
1083     *
1084     * @return array
1085     */
1086    public function get_theme_errors() {
1087        $themes_with_errors = wp_get_themes( array( 'errors' => true ) );
1088        $theme_errors       = array();
1089
1090        foreach ( $themes_with_errors as $theme ) {
1091            $errors = $theme->errors();
1092
1093            if ( is_wp_error( $errors ) && ! empty( $errors->get_error_messages() ) ) {
1094                $theme_errors[] = array(
1095                    'name'   => sanitize_title( $theme->get( 'Name' ) ),
1096                    'errors' => (array) $errors->get_error_messages(),
1097                );
1098            }
1099        }
1100
1101        return $theme_errors;
1102    }
1103
1104    /**
1105     * Gets the header image data.
1106     *
1107     * @return bool|object
1108     **/
1109    public function get_header_image() {
1110        return get_theme_mod( 'header_image_data' );
1111    }
1112
1113    /**
1114     * Gets the theme background color.
1115     *
1116     * @return bool|string
1117     **/
1118    public function get_background_color() {
1119        return get_theme_mod( 'background_color' );
1120    }
1121
1122    /**
1123     * Get the image default link type.
1124     *
1125     * @return string
1126     **/
1127    public function get_image_default_link_type() {
1128        return get_option( 'image_default_link_type' );
1129    }
1130
1131    /**
1132     * Gets the image thumbnails width.
1133     *
1134     * @return int
1135     **/
1136    public function get_image_thumbnail_width() {
1137        return (int) get_option( 'thumbnail_size_w' );
1138    }
1139
1140    /**
1141     * Gets the image thumbnails height.
1142     *
1143     * @return int
1144     **/
1145    public function get_image_thumbnail_height() {
1146        return (int) get_option( 'thumbnail_size_h' );
1147    }
1148
1149    /**
1150     * Whether cropping is enabled for thumbnails.
1151     *
1152     * @return string
1153     **/
1154    public function get_image_thumbnail_crop() {
1155        return get_option( 'thumbnail_crop' );
1156    }
1157
1158    /**
1159     * Gets the medium sized image setting's width.
1160     *
1161     * @return int
1162     **/
1163    public function get_image_medium_width() {
1164        return (int) get_option( 'medium_size_w' );
1165    }
1166
1167    /**
1168     * Gets the medium sized image setting's height.
1169     *
1170     * @return int
1171     **/
1172    public function get_image_medium_height() {
1173        return (int) get_option( 'medium_size_h' );
1174    }
1175
1176    /**
1177     * Gets the large sized image setting's width.
1178     *
1179     * @return int
1180     **/
1181    public function get_image_large_width() {
1182        return (int) get_option( 'large_size_w' );
1183    }
1184
1185    /**
1186     * Gets the large sized image setting's height.
1187     *
1188     * @return int
1189     **/
1190    public function get_image_large_height() {
1191        return (int) get_option( 'large_size_h' );
1192    }
1193
1194    /**
1195     * Gets the permalink structure as defined in the site's settings.
1196     *
1197     * @return string
1198     **/
1199    public function get_permalink_structure() {
1200        return get_option( 'permalink_structure' );
1201    }
1202
1203    /**
1204     * Gets the default post format
1205     *
1206     * @return string
1207     **/
1208    public function get_default_post_format() {
1209        return get_option( 'default_post_format' );
1210    }
1211
1212    /**
1213     * Gets the default post category
1214     *
1215     * @return int
1216     **/
1217    public function get_default_category() {
1218        return (int) get_option( 'default_category' );
1219    }
1220
1221    /**
1222     * Returns what should be shown on the front page (eg. page or posts)
1223     *
1224     * @return string
1225     **/
1226    public function get_show_on_front() {
1227        return get_option( 'show_on_front' );
1228    }
1229
1230    /**
1231     * Whether or not the front page is set as 'page' to allow a custom front page
1232     *
1233     * @return bool
1234     **/
1235    public function is_custom_front_page() {
1236        return ( 'page' === $this->get_show_on_front() );
1237    }
1238
1239    /**
1240     * Whether or not likes have been enabled on all site posts
1241     *
1242     * @return bool
1243     **/
1244    public function get_default_likes_enabled() {
1245        return (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
1246    }
1247
1248    /**
1249     * If sharing has been enabled and there are visible blog services (eg. 'facebook', 'twitter'), returns true.
1250     *
1251     * @return bool
1252     **/
1253    public function get_default_sharing_status() {
1254        $default_sharing_status = false;
1255        if ( class_exists( 'Sharing_Service' ) ) {
1256            $ss                     = new Sharing_Service();
1257            $blog_services          = $ss->get_blog_services();
1258            $default_sharing_status = ! empty( $blog_services['visible'] );
1259        }
1260        return $default_sharing_status;
1261    }
1262
1263    /**
1264     * Displays the current comment status
1265     *
1266     * @return bool  False if closed, true for all other comment statuses.
1267     **/
1268    public function get_default_comment_status() {
1269        return 'closed' !== get_option( 'default_comment_status' );
1270    }
1271
1272    /**
1273     * Displays the current site-wide post ping status (for pingbacks and trackbacks)
1274     *
1275     * @return bool  False if closed, true for all other ping statuses.
1276     **/
1277    public function default_ping_status() {
1278        return 'closed' !== get_option( 'default_ping_status' );
1279    }
1280
1281    /**
1282     * Whether or not Publicize has been permanently disabled on the site
1283     *
1284     * @see wpcom/wp-content/admin-plugins/publicize/publicize-wpcom.php
1285     *
1286     * @return bool  Default false.
1287     **/
1288    public function is_publicize_permanently_disabled() {
1289        $publicize_permanently_disabled = false;
1290        if ( function_exists( 'is_publicize_permanently_disabled' ) ) {
1291            $publicize_permanently_disabled = is_publicize_permanently_disabled( $this->blog_id );
1292        }
1293        return $publicize_permanently_disabled;
1294    }
1295
1296    /**
1297     * Returns the post ID of the static front page.
1298     *
1299     * @return int
1300     **/
1301    public function get_page_on_front() {
1302        return (int) get_option( 'page_on_front' );
1303    }
1304
1305    /**
1306     * Returns the post ID of the page designated as the posts page.
1307     *
1308     * @return int
1309     **/
1310    public function get_page_for_posts() {
1311        return (int) get_option( 'page_for_posts' );
1312    }
1313
1314    /**
1315     * Whether or not headstart is enabled for the site
1316     *
1317     * @return bool
1318     **/
1319    public function is_headstart() {
1320        return get_option( 'headstart' );
1321    }
1322
1323    /**
1324     * The WordPress version on the site.
1325     *
1326     * @return string
1327     **/
1328    public function get_wordpress_version() {
1329        global $wp_version;
1330        return $wp_version;
1331    }
1332
1333    /**
1334     * Whether or not this is a domain-only site (only relevant on WordPress.com simple sites - false otherwise)
1335     *
1336     * @return bool
1337     **/
1338    public function is_domain_only() {
1339        $options = get_option( 'options' );
1340        return ! empty( $options['is_domain_only'] ) ? (bool) $options['is_domain_only'] : false;
1341    }
1342
1343    /**
1344     * Whether or not the blog is set to public (not hidden from search engines)
1345     *
1346     * @return int 1 for true, 0 for false.
1347     **/
1348    public function get_blog_public() {
1349        return (int) get_option( 'blog_public' );
1350    }
1351
1352    /**
1353     * Whether or not the site is in a 'pending automated transfer' state.
1354     *
1355     * @return bool
1356     **/
1357    public function has_pending_automated_transfer() {
1358        /**
1359         * Filter if a site is in pending automated transfer state.
1360         *
1361         * @module json-api
1362         *
1363         * @since 6.4.0
1364         *
1365         * @param bool has_site_pending_automated_transfer( $this->blog_id )
1366         * @param int  $blog_id Blog identifier.
1367         */
1368        return apply_filters(
1369            'jetpack_site_pending_automated_transfer',
1370            false,
1371            $this->blog_id
1372        );
1373    }
1374
1375    /**
1376     * Whether or not the site has a 'designType' option set as 'store'
1377     *
1378     * @return bool
1379     **/
1380    public function signup_is_store() {
1381        return $this->get_design_type() === 'store';
1382    }
1383
1384    /**
1385     * Return a new WP_Roles instance, which implements a user roles API
1386     *
1387     * @return WP_Roles
1388     **/
1389    public function get_roles() {
1390        return new WP_Roles();
1391    }
1392
1393    /**
1394     * Returns the 'designType' option if set (the site design type), null otherwise.
1395     *
1396     * @return string|null
1397     **/
1398    public function get_design_type() {
1399        $options = get_option( 'options' );
1400        return empty( $options['designType'] ) ? null : $options['designType'];
1401    }
1402
1403    /**
1404     * Returns the 'site_goals' option if set (eg. share, promote, educate, sell, showcase).
1405     *
1406     * @return array
1407     **/
1408    public function get_site_goals() {
1409        $site_goals_option = get_option( 'site_goals' );
1410
1411        if ( is_array( $site_goals_option ) ) {
1412            return $site_goals_option;
1413        }
1414
1415        return array();
1416    }
1417    /**
1418     * Return site's launch status. Expanded in class.json-api-site-jetpack.php.
1419     *
1420     * @return bool False in this case.
1421     */
1422    public function get_launch_status() {
1423        return false;
1424    }
1425
1426    /**
1427     * Whether a site has any migration meta details - only applicable on WordPress.com
1428     *
1429     * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php
1430     *
1431     * @return null
1432     */
1433    public function get_migration_meta() {
1434        return null;
1435    }
1436
1437    /**
1438     * Whether a site has a site segment - only applicable on WordPress.com
1439     *
1440     * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php
1441     *
1442     * @return false
1443     */
1444    public function get_site_segment() {
1445        return false;
1446    }
1447
1448    /**
1449     * Whether a site has Vertical ID (used for Starter Templates) - default to only applicable on WordPress.com
1450     *
1451     * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php
1452     *
1453     * @return false
1454     */
1455    public function get_site_vertical_id() {
1456        return false;
1457    }
1458
1459    /**
1460     * Whether a site has a 'site_creation_flow' option set (eg gutenboarding, mobile) - only applicable on WordPress.com
1461     *
1462     * @see /wpcom-json-endpoints/class.wpcom-json-api-new-site-endpoint.php for more on the option.
1463     *
1464     * @return bool
1465     */
1466    public function get_site_creation_flow() {
1467        return get_option( 'site_creation_flow' );
1468    }
1469
1470    /**
1471     * Whether a site has a 'site_source_slug' option set - only applicable on WordPress.com
1472     *
1473     * @see /wpcom-json-endpoints/class.wpcom-json-api-new-site-endpoint.php for more on the option.
1474     *
1475     * @return bool
1476     */
1477    public function get_site_source_slug() {
1478            return get_option( 'site_source_slug' );
1479    }
1480
1481    /**
1482     * Return any selected features (used to help recommend plans)
1483     *
1484     * @return string
1485     */
1486    public function get_selected_features() {
1487        return get_option( 'selected_features' );
1488    }
1489
1490    /**
1491     * Return true if the site design was created with a Blank Canvas (empty homepage template), false otherwise.
1492     *
1493     * @return bool
1494     */
1495    public function was_created_with_blank_canvas_design() {
1496        return (bool) get_option( 'was_created_with_blank_canvas_design' );
1497    }
1498
1499    /**
1500     * Get the option storing the Anchor podcast ID that identifies a site as a podcasting site.
1501     *
1502     * @return string
1503     */
1504    public function get_anchor_podcast() {
1505        return get_option( 'anchor_podcast' );
1506    }
1507
1508    /**
1509     * Check if the site is currently being built by the DIFM Lite team.
1510     *
1511     * @return bool
1512     */
1513    public function is_difm_lite_in_progress() {
1514        if ( function_exists( 'has_blog_sticker' ) ) {
1515            return has_blog_sticker( 'difm-lite-in-progress' );
1516        } elseif ( function_exists( 'wpcomsh_is_site_sticker_active' ) ) {
1517            // For atomic sites
1518            return wpcomsh_is_site_sticker_active( 'difm-lite-in-progress' );
1519        }
1520        return false;
1521    }
1522
1523    /**
1524     * Get the DIFM Lite site options exposed to the frontend while a build is in progress.
1525     *
1526     * Returns null unless the site has an active DIFM Lite build and the code is
1527     * running on WordPress.com, where the DIFM Lite library exists.
1528     *
1529     * @return array|null
1530     */
1531    public function get_difm_lite_site_options() {
1532        if ( ! $this->is_difm_lite_in_progress() ) {
1533            return null;
1534        }
1535        if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM || ! function_exists( 'require_lib' ) ) {
1536            return null;
1537        }
1538        require_lib( 'difm-lite' );
1539        if ( ! class_exists( '\DIFM_Lite_Options' ) ) {
1540            // Fail closed if the library did not define the class; this method
1541            // documents null for every path where the options are unavailable.
1542            return null;
1543        }
1544        // The submission state is canonical on the purchase blog. This site may be
1545        // the stickered staging build target, whose own options blob is empty, so
1546        // resolve to the purchase blog before reading â€” otherwise a post-submit
1547        // staging target reports itself as pre-submit.
1548        //
1549        // resolve_purchase_site_id() ships in the wpcom DIFM Lite site-role
1550        // pointers change. This Jetpack code can deploy before that lands, so
1551        // fall back to the current blog when it is unavailable â€” no retargeted
1552        // builds can exist until that change is live, so the current blog is the
1553        // purchase blog in that window.
1554        $purchase_blog_id = $this->blog_id;
1555        // @phan-suppress-next-line PhanUndeclaredClassReference -- wpcom-only class, guarded above.
1556        if ( method_exists( '\DIFM_Lite_Options', 'resolve_purchase_site_id' ) ) {
1557            // @phan-suppress-next-line PhanUndeclaredClassMethod -- wpcom-only class, guarded above.
1558            $purchase_blog_id = \DIFM_Lite_Options::resolve_purchase_site_id( $this->blog_id );
1559        }
1560        // @phan-suppress-next-line PhanUndeclaredClassMethod -- wpcom-only class, guarded above.
1561        $difm_lite_options = new \DIFM_Lite_Options( $purchase_blog_id );
1562        return array(
1563            // @phan-suppress-next-line PhanUndeclaredClassProperty -- wpcom-only class, guarded above.
1564            'is_website_content_submitted' => (bool) $difm_lite_options->is_website_content_submitted,
1565        );
1566    }
1567
1568    /**
1569     * Check if the site has the gating-business-q1 blog sticker.
1570     *
1571     * @return bool
1572     */
1573    public function is_gating_business_q1() {
1574        if ( function_exists( 'has_blog_sticker' ) ) {
1575            return has_blog_sticker( 'gating-business-q1' );
1576        } elseif ( function_exists( 'wpcomsh_is_site_sticker_active' ) ) {
1577            // For atomic sites
1578            return wpcomsh_is_site_sticker_active( 'gating-business-q1' );
1579        }
1580        return false;
1581    }
1582
1583    /**
1584     * Whether the site is still on the pre-2026 feature gating.
1585     *
1586     * @return bool
1587     */
1588    public function is_legacy_gating_site() {
1589        if ( ! method_exists( 'WPCOM_Features', 'is_legacy_gating_site' ) ) {
1590            return false;
1591        }
1592
1593        return (bool) WPCOM_Features::is_legacy_gating_site( $this->blog_id );
1594    }
1595
1596    /**
1597     * Get the option of site intent which value is coming from the Hero Flow
1598     *
1599     * @return string
1600     */
1601    public function get_site_intent() {
1602        return get_option( 'site_intent', '' );
1603    }
1604
1605    /**
1606     * Get the option of site partner bundle which value is coming from the Partner Flow
1607     *
1608     * @return string
1609     */
1610    public function get_site_partner_bundle() {
1611        return get_option( 'site_partner_bundle', '' );
1612    }
1613
1614    /**
1615     * Get site option to determine if and how to display launchpad onboarding
1616     *
1617     * @return string
1618     */
1619    public function get_launchpad_screen() {
1620        return get_option( 'launchpad_screen' );
1621    }
1622
1623    /**
1624     * Get the option onboarding_segment coming from the Guided Flow
1625     *
1626     * @return string
1627     */
1628    public function get_onboarding_segment() {
1629        return get_option( 'onboarding_segment', '' );
1630    }
1631
1632    /**
1633     * Get site option for completed launchpad checklist tasks
1634     *
1635     * @return string
1636     */
1637    public function get_launchpad_checklist_tasks_statuses() {
1638        $launchpad_checklist_tasks_statuses_option = get_option( 'launchpad_checklist_tasks_statuses' );
1639
1640        if ( is_array( $launchpad_checklist_tasks_statuses_option ) ) {
1641            return $launchpad_checklist_tasks_statuses_option;
1642        }
1643
1644        return array();
1645    }
1646
1647    /**
1648     * Whether the AI Launchpad is enabled for this site. Mirrors AI_Launchpad::is_enabled_for_site() in jetpack-mu-wpcom.
1649     *
1650     * @return bool
1651     */
1652    public function is_ai_launchpad_enabled() {
1653        return (bool) get_option( 'wpcom_ai_launchpad_enabled' ) && ! $this->is_ai_launchpad_no_guidance();
1654    }
1655
1656    /**
1657     * Whether the AI Launchpad was dismissed (skipped), which leaves the site with no setup guidance.
1658     *
1659     * @return bool
1660     */
1661    public function is_ai_launchpad_dismissed() {
1662        return (bool) get_option( 'wpcom_ai_launchpad_dismissed' );
1663    }
1664
1665    /**
1666     * Whether every AI Launchpad task has been completed.
1667     *
1668     * @return bool
1669     */
1670    public function is_ai_launchpad_completed() {
1671        return (bool) get_option( 'wpcom_ai_launchpad_completed' );
1672    }
1673
1674    /**
1675     * Whether the site gets no setup guidance: no My Home, Site Setup, or launchpad. Mirrors
1676     * wpcom_launchpad_is_no_guidance() in jetpack-mu-wpcom.
1677     *
1678     * @return bool
1679     */
1680    public function is_ai_launchpad_no_guidance() {
1681        if ( get_option( 'wpcom_ai_launchpad_no_guidance' ) ) {
1682            return true;
1683        }
1684
1685        if ( get_option( 'wpcom_ai_launchpad_enabled' ) ) {
1686            return (bool) get_option( 'wpcom_ai_launchpad_dismissed' );
1687        }
1688
1689        return $this->is_past_no_guidance_cutoff();
1690    }
1691
1692    /**
1693     * Whether this Simple or Atomic site was created on or after WPCOM_LAUNCHPAD_NO_GUIDANCE_CUTOFF_BLOG_ID.
1694     *
1695     * Self-hosted Jetpack sites take blog IDs from the same sequence, so they never count.
1696     *
1697     * @return bool
1698     */
1699    protected function is_past_no_guidance_cutoff() {
1700        $cutoff = (int) Constants::get_constant( 'WPCOM_LAUNCHPAD_NO_GUIDANCE_CUTOFF_BLOG_ID' );
1701        if ( $cutoff <= 0 || ( $this->is_jetpack_connection() && ! $this->is_wpcom_atomic() ) ) {
1702            return false;
1703        }
1704
1705        return (int) $this->get_id() >= $cutoff;
1706    }
1707
1708    /**
1709     * Get site option for migration source site domain
1710     *
1711     * @return string
1712     */
1713    public function get_migration_source_site_domain() {
1714        return get_option( 'migration_source_site_domain', '' );
1715    }
1716
1717    /**
1718     * Detect whether a site is WordPress.com Staging Site.
1719     *
1720     * @see class.json-api-site-jetpack.php for implementation.
1721     */
1722    abstract public function is_wpcom_staging_site();
1723
1724    /**
1725     * Get site option for the production blog id (if is a WP.com Staging Site).
1726     *
1727     * @see class.json-api-site-jetpack.php for implementation.
1728     */
1729    abstract public function get_wpcom_production_blog_id();
1730
1731    /**
1732     * Get site option for the staging blog ids (if it has them)
1733     *
1734     * @see class.json-api-site-jetpack.php for implementation.
1735     */
1736    abstract public function get_wpcom_staging_blog_ids();
1737
1738    /**
1739     * Get the site's Blaze eligibility status.
1740     *
1741     * @return bool
1742     */
1743    public function can_blaze() {
1744        return (bool) Blaze::site_supports_blaze( $this->blog_id );
1745    }
1746
1747    /**
1748     * Return site's setup identifier.
1749     *
1750     * @return string
1751     */
1752    public function get_wpcom_site_setup() {
1753        return get_option( 'wpcom_site_setup' );
1754    }
1755
1756    /**
1757     * Returns whether the site is commercial.
1758     *
1759     * @return mixed
1760     *
1761     * - `true`: the site is commercial
1762     * - `false`: the site is not commercial
1763     * - `null`: the commercial status is not yet determined
1764     */
1765    public function is_commercial() {
1766        // Override if blog has the commercial stickers.
1767        if ( function_exists( 'has_blog_sticker' ) ) {
1768            $has_not_commercial_sticker = has_blog_sticker( 'jetpack-site-is-not-commercial-override', $this->blog_id );
1769            if ( $has_not_commercial_sticker ) {
1770                return false;
1771            }
1772            $has_commercial_sticker = has_blog_sticker( 'jetpack-site-is-commercial-override', $this->blog_id );
1773            if ( $has_commercial_sticker ) {
1774                return true;
1775            }
1776        }
1777
1778        $is_commercial = get_option( '_jetpack_site_is_commercial', null );
1779        return $is_commercial === null ? null : (bool) $is_commercial;
1780    }
1781
1782    /**
1783     * Returns an array of reasons why the site is considered commercial.
1784     *
1785     * @return array|null
1786     */
1787    public function get_is_commercial_reasons() {
1788        $reasons = get_option( '_jetpack_site_is_commercial_reason', array() );
1789
1790        // Add override as reason if blog has the commercial stickers.
1791        if ( empty( $reasons ) && $this->is_commercial() ) {
1792            return array( 'manual-override' );
1793        } elseif ( ! is_array( $reasons ) ) {
1794            return array();
1795        }
1796
1797        return $reasons;
1798    }
1799
1800    /**
1801     * Returns the site's interface selection e.g. calypso vs. wp-admin
1802     *
1803     * @return string
1804     **/
1805    public function get_wpcom_admin_interface() {
1806        return (string) get_option( 'wpcom_admin_interface' );
1807    }
1808
1809    /**
1810     * Returns whether the site is part of the classic view early release.
1811     *
1812     * @return bool
1813     **/
1814    public function get_wpcom_classic_early_release() {
1815        return ! empty( get_option( 'wpcom_classic_early_release' ) );
1816    }
1817
1818    /**
1819     * Returns whether APM (Application Performance Monitoring) is enabled for the site.
1820     *
1821     * APM is an Atomic-only hosting feature. Non-Atomic site types (Simple wpcom, real
1822     * Jetpack) return false; Jetpack_Shadow_Site overrides with the actual read.
1823     *
1824     * @return bool
1825     **/
1826    public function get_apm_enabled() {
1827        return false;
1828    }
1829
1830    /**
1831     * Get Zendesk site meta.
1832     *
1833     * @return array|null
1834     */
1835    abstract public function get_zendesk_site_meta();
1836
1837    /**
1838     * Detect whether there's a pending plan for this site.
1839     *
1840     * @return bool
1841     */
1842    abstract public function is_pending_plan();
1843
1844    /**
1845     * Detect whether the site is a Garden site.
1846     *
1847     * @return bool
1848     */
1849    public function is_garden() {
1850        return false;
1851    }
1852
1853    /**
1854     * Get the Garden name.
1855     *
1856     * @return string
1857     */
1858    public function garden_name() {
1859        return null;
1860    }
1861
1862    /**
1863     * Get the Garden partner.
1864     *
1865     * @return string
1866     */
1867    public function garden_partner() {
1868        return null;
1869    }
1870
1871    /**
1872     * Detect whether the Garden site is provisioned.
1873     *
1874     * @return bool|null
1875     */
1876    public function garden_is_provisioned() {
1877        return null;
1878    }
1879
1880    /**
1881     * Detect whether the site is a Flex site.
1882     *
1883     * @return bool
1884     */
1885    public function is_wpcom_flex() {
1886        if ( function_exists( 'has_blog_sticker' ) ) {
1887            return has_blog_sticker( 'flex-cache-site' );
1888        }
1889        return false;
1890    }
1891
1892    /**
1893     * Detect whether Big Sky AI assistant is enabled for this site.
1894     *
1895     * @return bool
1896     */
1897    public function is_big_sky_enabled() {
1898        return false;
1899    }
1900
1901    /**
1902     * Get the state of any block on the site's outgoing email.
1903     *
1904     * @return array|null `status` (`blocked` or `at_risk`), `reason` and `expires_on`,
1905     *                    or null if the site has never been blocked.
1906     */
1907    public function get_atomic_email_block() {
1908        return null;
1909    }
1910
1911    /**
1912     * Get Jetpack recovery mode status.
1913     *
1914     * @return array|null
1915     */
1916    public function get_jetpack_recovery_mode_status() {
1917        $status = get_option( 'jetpack_recovery_mode_status' );
1918        return is_array( $status ) ? $status : null;
1919    }
1920
1921    /**
1922     * Whether WordPress.com accounts must have two-step authentication to log in through SSO.
1923     *
1924     * @return bool
1925     */
1926    public function get_jetpack_sso_require_two_step() {
1927        /** This filter is documented in projects/packages/connection/src/sso/class-helpers.php */
1928        return (bool) apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step', false ) );
1929    }
1930}