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