Файловый менеджер - Редактировать - /var/www/xthruster/html/wp-content/uploads/flags/sal.tar
Назад
class.json-api-site-jetpack.php 0000644 00000045013 14722051450 0012465 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * This class extends the Abstract_Jetpack_Site class, which includes providing * the implementation for functions that were declared in that class. * * @see class.json-api-site-jetpack-base.php for more context on some of * the functions extended here. * * @package automattic/jetpack */ use Automattic\Jetpack\Status\Host; use Automattic\Jetpack\Sync\Functions; require_once __DIR__ . '/class.json-api-site-jetpack-base.php'; require_once __DIR__ . '/class.json-api-post-jetpack.php'; /** * Base class for Jetpack_Site. This code runs on Jetpack (.org) sites. */ class Jetpack_Site extends Abstract_Jetpack_Site { /** * Retrieves a Jetpack option's value, given the option name. * * @param string $name the name of the Jetpack option, without the 'jetpack' prefix (eg. 'log' for 'jetpack_log'). * * @return mixed */ protected function get_mock_option( $name ) { return get_option( 'jetpack_' . $name ); } /** * If a Jetpack constant name has been defined, this will return the value of the constant. * * @param string $name the name of the Jetpack constant to check. * * @return mixed */ protected function get_constant( $name ) { if ( defined( $name ) ) { return constant( $name ); } return null; } /** * Returns the site URL for the current network. * * @return string */ protected function main_network_site() { return network_site_url(); } /** * Returns the WordPress version for the current site. * * @return string */ protected function wp_version() { global $wp_version; return $wp_version; } /** * Returns the maximum upload size allowed in php.ini. * * @return int */ protected function max_upload_size() { return wp_max_upload_size(); } /** * This function returns the value of the 'WP_MEMORY_LIMIT' constant converted to an integer byte value. * * @return int */ protected function wp_memory_limit() { return wp_convert_hr_to_bytes( WP_MEMORY_LIMIT ); } /** * This function returns the value of the 'WP_MAX_MEMORY_LIMIT' constant converted to an integer byte value. * * @return int */ protected function wp_max_memory_limit() { return wp_convert_hr_to_bytes( WP_MAX_MEMORY_LIMIT ); } /** * Returns true if the site is within a system with a multiple networks, false otherwise. * * @see /projects/packages/status/src/class-status.php * * @return bool */ protected function is_main_network() { return Jetpack::is_multi_network(); } /** * Returns true if Multisite is enabled, false otherwise. * * @return bool */ public function is_multisite() { return (bool) is_multisite(); } /** * Returns true if the current site is a single user site, false otherwise. * * @return bool */ public function is_single_user_site() { return (bool) Jetpack::is_single_user_site(); } /** * Returns true if is_vcs_checkout discovers a version control checkout, false otherwise. * * @see projects/packages/sync/src/class-functions.php. * * @return bool */ protected function is_version_controlled() { return Functions::is_version_controlled(); } /** * Returns true if the site has file write access, false otherwise. * * @see projects/packages/sync/src/class-functions.php. * * @return bool */ protected function file_system_write_access() { return Functions::file_system_write_access(); } /** * Returns true if the current theme supports the $feature_name, false otherwise. * * @param string $feature_name the name of the Jetpack feature. * * @return bool */ protected function current_theme_supports( $feature_name ) { return current_theme_supports( $feature_name ); } /** * Gets theme support arguments to be checked against the specific Jetpack feature. * * @param string $feature_name the name of the Jetpack feature to check against. * * @return array */ protected function get_theme_support( $feature_name ) { return get_theme_support( $feature_name ); } /** * Fetch a list of active plugins that are using Jetpack Connection. * * @return array An array of active plugins (by slug) that are using Jetpack Connection. */ protected function get_connection_active_plugins() { $plugins = $this->get_mock_option( 'connection_active_plugins' ); return is_array( $plugins ) ? array_keys( $plugins ) : array(); } /** * Gets updates and then stores them in the jetpack_updates option, returning an array with the option schema. * * @return array */ public function get_updates() { return (array) Jetpack::get_updates(); } /** * Returns the Jetpack blog ID for a site. * * @return int */ public function get_id() { return $this->platform->token->blog_id; } /** * Returns true if a site has the 'videopress' option enabled, false otherwise. * * @return bool */ public function has_videopress() { // TODO - this only works on wporg site - need to detect videopress option for remote Jetpack site on WPCOM. $videopress = Jetpack_Options::get_option( 'videopress', array() ); if ( isset( $videopress['blog_id'] ) && $videopress['blog_id'] > 0 ) { return true; } return false; } /** * Returns VideoPress storage used, in MB. * * @see class.json-api-site-jetpack-shadow.php on WordPress.com for implementation. Only applicable on WordPress.com. * * @return float */ public function get_videopress_storage_used() { return 0; } /** * Sets the upgraded_filetypes_enabled Jetpack option to true as a default. * * Only relevant for WordPress.com sites: * See wpcom_site_has_upgraded_upload_filetypes at /wpcom/wp-content/mu-plugins/misc.php. * * @return bool */ public function upgraded_filetypes_enabled() { return true; } /** * Sets the is_mapped_domain Jetpack option to true as a default. * * Primarily used in WordPress.com to confirm the current blog's domain does or doesn't match the primary redirect. * * @see /wpcom/wp-content/mu-plugins/insecure-content-helpers.php within WordPress.com. * * @return bool */ public function is_mapped_domain() { return true; } /** * Fallback to the home URL since all Jetpack sites don't have an unmapped *.wordpress.com domain. * * @return string */ public function get_unmapped_url() { // Fallback to the home URL since all Jetpack sites don't have an unmapped *.wordpress.com domain. return $this->get_url(); } /** * Whether the domain is a site redirect or not. Defaults to false on a Jetpack site. * * 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. * * @see /wpcom/wp-includes/Requests/Response.php within WordPress.com. * * @return bool */ public function is_redirect() { return false; } /** * Whether or not the current user is following this blog. Defaults to false. * * @return bool */ public function is_following() { return false; } /** * Points to the user ID of the site owner * * @return null for Jetpack sites */ public function get_site_owner() { return null; } /** * Whether or not the Jetpack 'wordads' module is active on the site. * * @return bool */ public function has_wordads() { return Jetpack::is_module_active( 'wordads' ); } /** * Defaults to false on Jetpack sites, however is used on WordPress.com sites. This nonce is used for previews on Jetpack sites. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. * * @return bool */ public function get_frame_nonce() { return false; } /** * Defaults to false on Jetpack sites, however is used on WordPress.com sites, * where it creates a nonce to be used with iframed block editor requests to a Jetpack site. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. * * @return bool */ public function get_jetpack_frame_nonce() { return false; } /** * 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. * * @see /wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @return bool */ public function is_headstart_fresh() { return false; } /** * Returns the allowed mime types and file extensions for a site. * * @return array */ public function allowed_file_types() { $allowed_file_types = array(); // https://codex.wordpress.org/Uploading_Files. $mime_types = get_allowed_mime_types(); foreach ( $mime_types as $type => $mime_type ) { $extras = explode( '|', $type ); foreach ( $extras as $extra ) { $allowed_file_types[] = $extra; } } return $allowed_file_types; } /** * Return site's privacy status. * * @return bool Is site private? */ public function is_private() { return (int) $this->get_atomic_cloud_site_option( 'blog_public' ) === -1; } /** * Return site's coming soon status. * * @return bool Is site "Coming soon"? */ public function is_coming_soon() { return $this->is_private() && (int) $this->get_atomic_cloud_site_option( 'wpcom_coming_soon' ) === 1; } /** * Return site's launch status. * * @return string|bool Launch status ('launched', 'unlaunched', or false). */ public function get_launch_status() { return $this->get_atomic_cloud_site_option( 'launch-status' ); } /** * Given an option name, returns false if the site isn't WoA or doesn't have the ability to retrieve cloud site options. * Otherwise, if the option name exists amongst Jetpack options, the option value is returned. * * @param string $option The option name to check. * * @return string|bool */ public function get_atomic_cloud_site_option( $option ) { if ( ! ( new Host() )->is_woa_site() ) { return false; } $jetpack = Jetpack::init(); if ( ! method_exists( $jetpack, 'get_cloud_site_options' ) ) { return false; } $result = $jetpack->get_cloud_site_options( array( $option ) ); if ( ! array_key_exists( $option, $result ) ) { return false; } return $result[ $option ]; } /** * Defaults to false instead of returning the current site plan. * * @return bool */ public function get_plan() { return false; } /** * Defaults to 0 for the number of WordPress.com subscribers - this is filled in on the WordPress.com side. * * @see /wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @return int */ public function get_subscribers_count() { return 0; } /** * Defaults to false - this is filled on the WordPress.com side in multiple locations. * * @see WPCOM_JSON_API_GET_Site_Endpoint::decorate_jetpack_response. * @return bool */ public function get_capabilities() { return false; } /** * Returns the language code for the current site. * * @return string */ public function get_locale() { return get_bloginfo( 'language' ); } /** * The flag indicates that the site has Jetpack installed. * * @return bool */ public function is_jetpack() { return true; } /** * The flag indicates that the site is connected to WP.com via Jetpack Connection. * * @return bool */ public function is_jetpack_connection() { return true; } /** * Returns the current site's Jetpack version. * * @return string */ public function get_jetpack_version() { return JETPACK__VERSION; } /** * Empty function declaration - this function is filled out on the WordPress.com side, returning true if the site has an AK / VP bundle. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. */ public function get_ak_vp_bundle_enabled() {} /** * Returns the front page meta description for current site. * * @see /modules/seo-tools/class-jetpack-seo-utils.php. * * @return string */ public function get_jetpack_seo_front_page_description() { return Jetpack_SEO_Utils::get_front_page_meta_description(); } /** * Returns custom title formats from site option. * * @see /modules/seo-tools/class-jetpack-seo-titles.php. * * @return array */ public function get_jetpack_seo_title_formats() { return Jetpack_SEO_Titles::get_custom_title_formats(); } /** * Returns website verification codes. Allowed keys include: google, pinterest, bing, yandex, facebook. * * @see /modules/verification-tools/blog-verification-tools.php. * * @return array */ public function get_verification_services_codes() { return get_option( 'verification_services_codes', null ); } /** * Returns null for Jetpack sites. For WordPress.com sites this returns the value of the 'podcasting_archive' option. * * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php. * * @return null */ public function get_podcasting_archive() { return null; } /** * Defaulting to true, this function is expanded out on the WordPress.com side, returning an error if the site is not connected or not communicating to us. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. * * @return bool */ public function is_connected_site() { return true; } /** * 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. * * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php. * * @return bool */ public function is_wpforteams_site() { return false; } /** * Returns true if a user has got the capability that is being checked, false otherwise. * * @param string $role The capability to check. * * @return bool */ public function current_user_can( $role ) { return current_user_can( $role ); } /** * Check if full site editing should be considered as currently active. Full site editing * requires the FSE plugin to be installed and activated, as well the current * theme to be FSE compatible. The plugin can also be explicitly disabled via the * a8c_disable_full_site_editing filter. * * @since 7.7.0 * * @return bool true if full site editing is currently active. */ public function is_fse_active() { if ( ! Jetpack::is_plugin_active( 'full-site-editing/full-site-editing-plugin.php' ) ) { return false; } if ( function_exists( '\Automattic\Jetpack\Jetpack_Mu_Wpcom\Wpcom_Legacy_FSE\is_full_site_editing_active' ) ) { // @phan-suppress-next-line PhanUndeclaredFunction return \Automattic\Jetpack\Jetpack_Mu_Wpcom\Wpcom_Legacy_FSE\is_full_site_editing_active(); } return function_exists( '\A8C\FSE\is_full_site_editing_active' ) && \A8C\FSE\is_full_site_editing_active(); } /** * Check if site should be considered as eligible for full site editing. Full site editing * requires the FSE plugin to be installed and activated. For this method to return true * the current theme does not need to be FSE compatible. The plugin can also be explicitly * disabled via the a8c_disable_full_site_editing filter. * * @since 8.1.0 * * @return bool true if site is eligible for full site editing */ public function is_fse_eligible() { if ( ! Jetpack::is_plugin_active( 'full-site-editing/full-site-editing-plugin.php' ) ) { return false; } if ( function_exists( '\Automattic\Jetpack\Jetpack_Mu_Wpcom\Wpcom_Legacy_FSE\is_site_eligible_for_full_site_editing' ) ) { // @phan-suppress-next-line PhanUndeclaredFunction return \Automattic\Jetpack\Jetpack_Mu_Wpcom\Wpcom_Legacy_FSE\is_site_eligible_for_full_site_editing(); } return function_exists( '\A8C\FSE\is_site_eligible_for_full_site_editing' ) && \A8C\FSE\is_site_eligible_for_full_site_editing(); } /** * Check if site should be considered as eligible for use of the core Site Editor. * The Site Editor requires a block based theme to be active. * * @since 12.2 Uses wp_is_block_theme() to determine if site is eligible instead of gutenberg_is_fse_theme(). * @return bool true if site is eligible for the Site Editor */ public function is_core_site_editor_enabled() { return wp_is_block_theme(); } /** * Return the last engine used for an import on the site. Not used in Jetpack. * * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php. * * @return null */ public function get_import_engine() { return null; } /** * Post functions */ /** * Wrap a WP_Post object with SAL methods, returning a Jetpack_Post object. * * @param WP_Post $post A WP_Post object. * @param string $context The post request context (for example 'edit' or 'display'). * * @return Jetpack_Post */ public function wrap_post( $post, $context ) { return new Jetpack_Post( $this, $post, $context ); } /** * Get the option storing the Anchor podcast ID that identifies a site as a podcasting site. * * @return string */ public function get_anchor_podcast() { return $this->get_atomic_cloud_site_option( 'anchor_podcast' ); } /** * Get user interactions with a site. Not used in Jetpack. * * @see /wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @return null */ public function get_user_interactions() { return null; } /** * Get site deleted status. Not used in Jetpack. * * @see /wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @return bool */ public function is_deleted() { return false; } /** * Indicates that a site is an A4A client. Not used in Jetpack. * * @see /wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @return bool */ public function is_a4a_client() { return false; } /** * Detect whether a site is WordPress.com Staging Site. Not used in Jetpack. * * @see /wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @return false */ public function is_wpcom_staging_site() { return false; } /** * Get site option for the production blog id (if is a WP.com Staging Site). Not used in Jetpack. * * @see /wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @return null */ public function get_wpcom_production_blog_id() { return null; } /** * Get site option for the staging blog ids (if it has them). Not used in Jetpack. * * @see /wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @return null */ public function get_wpcom_staging_blog_ids() { return null; } /** * Get site option for the admin interface on WordPress.com Atomic sites. Not used in Jetpack. * * @return null */ public function get_wpcom_admin_interface() { return null; } /** * Get Zendesk site meta. Not used in Jetpack. * * @return null */ public function get_zendesk_site_meta() { return null; } /** * Detect whether there's a pending plan for this site. Not used in Jetpack. * * @return false */ public function is_pending_plan() { return false; } } class.json-api-token.php 0000644 00000007223 14722051450 0011223 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * SAL_Token class * * @package automattic/jetpack */ /** * Base class for Jetpack_Site, so that we have a real class instead of just passing around an array. */ class SAL_Token { /** * The Jetpack blog ID for the site. * * @var int */ public $blog_id; /** * The Jetpack user's user ID. * * @var int */ public $user_id; /** * The scope for the token, for example global or auth. * * @var string */ public $scope; /** * The Client ID (or WordPress.com Blog ID of this site. * * @var int */ public $client_id; /** * The user ID on the local site. * * @var int */ public $external_user_id; /** * Used for tokens created by Oauth clients. * * @var string */ public $external_user_code; /** * The type of authorization based on where the Jetpack connection is made - eg 'calypso', 'jetpack', 'client'. * * @var string */ public $auth_type; /** * Contructs the SAL_Token instance. * * @param int $blog_id The Jetpack blog ID for the site. * @param int $user_id The Jetpack user's user ID. * @param string $scope The scope for the token, for example global or auth. * @param int $client_id The Client ID (or WordPress.com Blog ID of this site. * @param int $external_user_id The user ID on the local site. * @param string $external_user_code Used for tokens created by Oauth clients. * @param string $auth_type The type of authorization based on where the Jetpack connection is made (eg. calypso). */ public function __construct( $blog_id, $user_id, $scope, $client_id, $external_user_id, $external_user_code, $auth_type ) { $this->blog_id = $blog_id; // if blog_id is set and scope is not global, limit to that blog. $this->user_id = $user_id; $this->client_id = $client_id; $this->scope = $scope; $this->external_user_id = $external_user_id; $this->external_user_code = $external_user_code; $this->auth_type = $auth_type; } /** * Checks if the scope is 'global'. * * @return bool */ public function is_global() { return $this->scope === 'global'; } /** * This function is used to create a SAL_Token instance with only a user id, if a token doesn't already exist. * * @return SAL_Token */ public static function for_anonymous_user() { return new SAL_Token( null, get_current_user_id(), null, // there's only ever one scope in our current API implementation, auth or global. null, null, null, null ); } /** * If a user token exists, the information is used to construct a SAL_Token with the correct parameters. * * @param array $token An array of details relevant to the connected user (may be empty). * * @return SAL_Token */ public static function from_rest_token( $token ) { $user_id = isset( $token['user_id'] ) ? $token['user_id'] : get_current_user_id(); $scope = isset( $token['scope'][0] ) ? $token['scope'][0] : null; $client_id = isset( $token['client_id'] ) ? $token['client_id'] : null; $external_user_id = isset( $token['external_user_id'] ) ? $token['external_user_id'] : null; $external_user_code = isset( $token['external_user_code'] ) ? $token['external_user_code'] : null; $auth = isset( $token['auth'] ) ? $token['auth'] : null; $blog_id = isset( $token['blog_id'] ) ? $token['blog_id'] : null; return new SAL_Token( $blog_id, $user_id, $scope, // there's only ever one scope in our current API implementation, auth or global. $client_id, $external_user_id, $external_user_code, $auth ); } } class.json-api-platform.php 0000644 00000002404 14722051450 0011723 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * SAL_Platform class which defines a token to later be associated with a Jetpack site * * @package automattic/jetpack */ require_once __DIR__ . '/class.json-api-token.php'; /** * Base class for SAL_Platform */ abstract class SAL_Platform { /** * A token that will represent a SAL_Token instance, default is empty. * * @var SAL_Token */ public $token; /** * Contructs the SAL_Platform instance * * @param SAL_Token $token The variable which will store the SAL_Token instance. */ public function __construct( $token ) { if ( is_array( $token ) ) { $token = SAL_Token::from_rest_token( $token ); } else { $token = SAL_Token::for_anonymous_user(); } $this->token = $token; } /** * This is the get_site function declaration, initially not implemented. * * @param int $blog_id The sites Jetpack blog ID. * @see class.json-api-platform-jetpack.php for the implementation of this function. */ abstract public function get_site( $blog_id ); } if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { require_once dirname( WP_CONTENT_DIR ) . '/public.api/rest/sal/class.json-api-platform-wpcom.php'; } else { require_once __DIR__ . '/class.json-api-platform-jetpack.php'; } class.json-api-metadata.php 0000644 00000004132 14722051450 0011657 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * WPCOM_JSON_API_Metadata class - Utility classes that don't necessarily have a home yet. * * @package automattic/jetpack */ /** * Base class for WPCOM_JSON_API_Metadata */ class WPCOM_JSON_API_Metadata { /** * Checks to see if a meta key is in the array of allowed public (and whitelisted) meta data. * * Additionally, if the key begins with 'geo_' or '_wpas_', true will also be returned. * * @param string $key A post metadata key value to check. * @return bool True or false depending on whether the key meets the defined criteria. **/ public static function is_public( $key ) { if ( empty( $key ) ) { return false; } // Default whitelisted meta keys. $whitelisted_meta = array( '_thumbnail_id' ); // whitelist of metadata that can be accessed. /** This filter is documented in json-endpoints/class.wpcom-json-api-post-endpoint.php */ if ( in_array( $key, apply_filters( 'rest_api_allowed_public_metadata', $whitelisted_meta ), true ) ) { return true; } if ( str_starts_with( $key, 'geo_' ) ) { return true; } if ( str_starts_with( $key, '_wpas_' ) ) { return true; } return false; } /** * Checks to see if a meta key should be used internally only. * * @param string $key A post metadata key value to check. * @return bool True or false depending on whether the key meets the defined criteria. **/ public static function is_internal_only( $key ) { // We want to always return the `_jetpack_blogging_prompt_key` key in post responses if it is available. if ( $key === '_jetpack_blogging_prompt_key' ) { return false; } // We want to always return the `_jetpack_newsletter_access` key to // display the correct newsletter access in Calypso. $whitelist = array( '_jetpack_newsletter_access', '_jetpack_newsletter_tier_id', ); if ( in_array( $key, $whitelist, true ) ) { return false; } if ( str_starts_with( $key, '_jetpack_' ) ) { return true; } if ( str_starts_with( $key, '_elasticsearch_' ) ) { return true; } return false; } } class.json-api-site-jetpack-base.php 0000644 00000025514 14722051450 0013401 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * This class extends the SAL_Site class, providing the implementation for * functions that were declared in that SAL_Site class as well as defining * base functions to be implemented in class Jetpack_Site. * * @see class.json-api-site-jetpack.php for more context on * the functions extended here. * * @package automattic/jetpack */ require_once __DIR__ . '/class.json-api-site-base.php'; /** * Base class for Abstract_Jetpack_Site. */ abstract class Abstract_Jetpack_Site extends SAL_Site { /** * Defining a base get_constant() function to be extended in the Jetpack_Site class. * * If a Jetpack constant name has been defined, this will return the value of the constant. * * @param string $name the name of the Jetpack constant to check. */ abstract protected function get_constant( $name ); /** * Defining a base current_theme_supports() function to be extended in the Jetpack_Site class. * * Returns true if the current theme supports the $feature_name, false otherwise. * * @param string $feature_name the name of the Jetpack feature. */ abstract protected function current_theme_supports( $feature_name ); /** * Defining a base get_theme_support() function to be extended in the Jetpack_Site class. * * Gets theme support arguments to be checked against the specific Jetpack feature. * * @param string $feature_name the name of the Jetpack feature to check against. */ abstract protected function get_theme_support( $feature_name ); /** * Defining a base get_mock_option() function to be extended in the Jetpack_Site class. * * Retrieves a Jetpack option's value, given the option name. * * @param string $name the name of the Jetpack option, without the 'jetpack' prefix (eg. 'log' for 'jetpack_log'). */ abstract protected function get_mock_option( $name ); /** * Defining a base get_jetpack_version() function to be extended in the Jetpack_Site class. * * Returns the current Jetpack version number. */ abstract public function get_jetpack_version(); /** * Defining a base get_updates() function to be extended in the Jetpack_Site class. * * Gets updates and then stores them in the jetpack_updates option, returning an array with the option schema. */ abstract public function get_updates(); /** * Defining a base main_network_site() function to be extended in the Jetpack_Site class. * * Returns the site URL for the current network. */ abstract protected function main_network_site(); /** * Defining a base wp_version() function to be extended in the Jetpack_Site class. * * Returns the WordPress version for the current site. */ abstract protected function wp_version(); /** * Defining a base max_upload_size() function to be extended in the Jetpack_Site class. * * Returns the maximum upload size allowed in php.ini. */ abstract protected function max_upload_size(); /** * Defining a base is_main_network() function to be extended in the Jetpack_Site class. * * Returns true if the site is within a system with a multiple networks, false otherwise. * * @see /projects/packages/status/src/class-status.php. */ abstract protected function is_main_network(); /** * Defining a base is_version_controlled() function to be extended in the Jetpack_Site class. * * Returns true if is_vcs_checkout discovers a version control checkout, false otherwise. * * @see projects/packages/sync/src/class-functions.php. */ abstract protected function is_version_controlled(); /** * Defining a base file_system_write_access() function to be extended in the Jetpack_Site class. * * Returns true if the site has file write access false otherwise. * * @see projects/packages/sync/src/class-functions.php. */ abstract protected function file_system_write_access(); /** * Fetch a list of active plugins that are using Jetpack Connection. */ abstract protected function get_connection_active_plugins(); /** * This function is implemented on WPCom sites, where a filter is removed which forces the URL to http. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. */ public function before_render() { } /** * This function returns the value of the 'WP_MEMORY_LIMIT' constant. * * @return int|string */ protected function wp_memory_limit() { return $this->get_constant( 'WP_MEMORY_LIMIT' ); } /** * This function returns the value of the 'WP_MAX_MEMORY_LIMIT' constant. * * @return int|string */ protected function wp_max_memory_limit() { return $this->get_constant( 'WP_MAX_MEMORY_LIMIT' ); } /** * If a user has manage options permissions and the site is the main site of the network, make updates visible. * * Called after response_keys have been rendered, which itself is used to return all the necessary information for a site’s response. * * @param array $response an array of the response keys. */ public function after_render( &$response ) { if ( current_user_can( 'manage_options' ) && $this->is_main_site( $response ) ) { $jetpack_update = $this->get_updates(); if ( ! empty( $jetpack_update ) ) { // In previous version of Jetpack 3.4, 3.5, 3.6 we synced the wp_version into to jetpack_updates. unset( $jetpack_update['wp_version'] ); // In previous version of Jetpack 3.4, 3.5, 3.6 we synced the site_is_version_controlled into to jetpack_updates. unset( $jetpack_update['site_is_version_controlled'] ); $response['updates'] = $jetpack_update; } } } /** * Extends the Jetpack options array with details including site constraints, WordPress and Jetpack versions, and plugins using the Jetpack connection. * * @param array $options an array of the Jetpack options. */ public function after_render_options( &$options ) { $options['jetpack_version'] = $this->get_jetpack_version(); $main_network_site = $this->main_network_site(); if ( $main_network_site ) { $options['main_network_site'] = (string) rtrim( $main_network_site, '/' ); } $active_modules = Jetpack_Options::get_option( 'active_modules' ); if ( is_array( $active_modules ) ) { $options['active_modules'] = (array) array_values( $active_modules ); } $options['software_version'] = (string) $this->wp_version(); $options['max_upload_size'] = $this->max_upload_size(); $options['wp_memory_limit'] = $this->wp_memory_limit(); $options['wp_max_memory_limit'] = $this->wp_max_memory_limit(); // Sites have to prove that they are not main_network site. // If the sync happends right then we should be able to see that we are not dealing with a network site. $options['is_multi_network'] = (bool) $this->is_main_network(); $options['is_multi_site'] = (bool) $this->is_multisite(); $file_mod_disabled_reasons = array_keys( array_filter( array( 'automatic_updater_disabled' => (bool) $this->get_constant( 'AUTOMATIC_UPDATER_DISABLED' ), // WP AUTO UPDATE CORE defaults to minor, '1' if true and '0' if set to false. 'wp_auto_update_core_disabled' => ! ( (bool) $this->get_constant( 'WP_AUTO_UPDATE_CORE' ) ), 'is_version_controlled' => (bool) $this->is_version_controlled(), // By default we assume that site does have system write access if the value is not set yet. 'has_no_file_system_write_access' => ! (bool) $this->file_system_write_access(), 'disallow_file_mods' => (bool) $this->get_constant( 'DISALLOW_FILE_MODS' ), ) ) ); $options['file_mod_disabled'] = empty( $file_mod_disabled_reasons ) ? false : $file_mod_disabled_reasons; $options['jetpack_connection_active_plugins'] = $this->get_connection_active_plugins(); } /** * This function returns the values of any active Jetpack modules. * * @return array */ public function get_jetpack_modules() { return array_values( Jetpack_Options::get_option( 'active_modules', array() ) ); } /** * This function returns true if a specified Jetpack module is active, false otherwise. * * @param string $module The Jetpack module name to check. * * @return bool */ public function is_module_active( $module ) { return in_array( $module, Jetpack_Options::get_option( 'active_modules', array() ), true ); } /** * This function returns false for a check as to whether a site is a VIP site or not. * * @return bool Always returns false. */ public function is_vip() { return false; // this may change for VIP Go sites, which sync using Jetpack. } /** * If the site's current theme supports post thumbnails, return true (otherwise return false). * * @return bool */ public function featured_images_enabled() { return $this->current_theme_supports( 'post-thumbnails' ); } /** * Returns an array of supported post formats. * * @return array */ public function get_post_formats() { // deprecated - see separate endpoint. get a list of supported post formats. $all_formats = get_post_format_strings(); $supported = $this->get_theme_support( 'post-formats' ); $supported_formats = array(); if ( isset( $supported[0] ) ) { foreach ( $supported[0] as $format ) { $supported_formats[ $format ] = $all_formats[ $format ]; } } return $supported_formats; } /** * Returns an array with site icon details. * * @return array */ public function get_icon() { $icon_id = get_option( 'site_icon' ); if ( empty( $icon_id ) ) { $icon_id = Jetpack_Options::get_option( 'site_icon_id' ); } if ( empty( $icon_id ) ) { return null; } $icon = array_filter( array( 'img' => wp_get_attachment_image_url( $icon_id, 'full' ), 'ico' => wp_get_attachment_image_url( $icon_id, array( 16, 16 ) ), ) ); if ( empty( $icon ) ) { return null; } if ( current_user_can( 'edit_posts', $icon_id ) ) { $icon['media_id'] = (int) $icon_id; } return $icon; } /** * Private methods **/ /** * This function returns true if the current site is the main network site, false otherwise. * * @param array $response The array of Jetpack response keys. * * @return bool */ private function is_main_site( $response ) { if ( isset( $response['options']->main_network_site ) && isset( $response['options']->unmapped_url ) ) { $main_network_site_url = set_url_scheme( $response['options']->main_network_site, 'http' ); $unmapped_url = set_url_scheme( $response['options']->unmapped_url, 'http' ); if ( $unmapped_url === $main_network_site_url ) { return true; } } return false; } /** * For Jetpack sites this will always return false. * * This is extended for WordPress.com sites in wpcom/public.api/rest/sal/trait.json-api-site-wpcom.php. * * @param int $post_id The post id. * * @return bool */ protected function is_a8c_publication( $post_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Extended and used in WordPress.com. return false; } } class.json-api-links.php 0000644 00000037007 14722051450 0011226 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * WPCOM_JSON_API_Links class. * * @package automattic/jetpack */ require_once __DIR__ . '/../class.json-api.php'; /** * Base class for WPCOM_JSON_API_Links. */ class WPCOM_JSON_API_Links { /** * An instance of the WPCOM_JSON_API. * * @var WPCOM_JSON_API */ private $api; /** * A WPCOM_JSON_API_Links instance. * * @var WPCOM_JSON_API_Links */ private static $instance; /** * An array of the closest supported version of an endpoint to the current endpoint. * * @var array */ private $closest_endpoint_cache_by_version = array(); /** * An array including the current api endpoint as well as the max versions found if that endpoint doesn't exist. * * @var array */ private $matches_by_version = array(); /** * An array including the cached endpoint path versions. * * @var array */ private $cache_result = null; /** * Creates a new instance of the WPCOM_JSON_API_Links class. * * @return WPCOM_JSON_API_Links */ public static function getInstance() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } /** * WPCOM_JSON_API_Links constructor. * * Method protected for singleton. */ protected function __construct() { $this->api = WPCOM_JSON_API::init(); } /** * An empty, private __clone method to prohibit cloning of this instance. */ private function __clone() { } /** * Overriding PHP's default __wakeup method to prvent unserializing of the instance, and return an error message. * * @return never */ public function __wakeup() { die( "Please don't __wakeup WPCOM_JSON_API_Links" ); } /** * Generate a URL to an endpoint * * Used to construct meta links in API responses * * @param mixed ...$args Optional arguments to be appended to URL. * @return string Endpoint URL **/ public function get_link( ...$args ) { $format = array_shift( $args ); $base = WPCOM_JSON_API__BASE; $path = array_pop( $args ); if ( $path ) { $path = '/' . ltrim( $path, '/' ); // tack the path onto the end of the format string. // have to escape %'s in the path as %% because // we're about to pass it through sprintf and we don't // want it to see the % as a placeholder. $format .= str_replace( '%', '%%', $path ); } // Escape any % in args before using sprintf. $escaped_args = array(); foreach ( $args as $arg_key => $arg_value ) { $escaped_args[ $arg_key ] = str_replace( '%', '%%', $arg_value ); } $relative_path = vsprintf( $format, $escaped_args ); if ( ! wp_startswith( $relative_path, '.' ) ) { // Generic version. Match the requested version as best we can. $api_version = $this->get_closest_version_of_endpoint( $format, $relative_path ); $base = substr( $base, 0, - 1 ) . $api_version; } // escape any % in the relative path before running it through sprintf again. $relative_path = str_replace( '%', '%%', $relative_path ); // http, WPCOM_JSON_API__BASE, ... , path. // %s , %s , $format, %s. return esc_url_raw( sprintf( "https://%s$relative_path", $base ) ); } /** * Generate the /me prefixed endpoint URL * * Used to construct meta links in API responses, specific to WordPress.com user account pages. * * @param string $path Optional path to be appended to the URL. * @return string /me endpoint URL **/ public function get_me_link( $path = '' ) { return $this->get_link( '/me', $path ); } /** * Generate the endpoint URL for taxonomies * * Used to construct meta links in API responses, specific to taxonomies. * * @param int $blog_id The site's Jetpack blog ID. * @param int $taxonomy_id The taxonomy ID (for example of the category, tag). * @param string $taxonomy_type The taxonomy type (for example category, tag). * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including taxonomy information. **/ public function get_taxonomy_link( $blog_id, $taxonomy_id, $taxonomy_type, $path = '' ) { switch ( $taxonomy_type ) { case 'category': return $this->get_link( '/sites/%d/categories/slug:%s', $blog_id, $taxonomy_id, $path ); case 'post_tag': return $this->get_link( '/sites/%d/tags/slug:%s', $blog_id, $taxonomy_id, $path ); default: return $this->get_link( '/sites/%d/taxonomies/%s/terms/slug:%s', $blog_id, $taxonomy_type, $taxonomy_id, $path ); } } /** * Generate the endpoint URL for media links * * Used to construct meta links in API responses, specific to media links. * * @param int $blog_id The site's Jetpack blog ID. * @param int $media_id The media item ID. * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including media information. **/ public function get_media_link( $blog_id, $media_id, $path = '' ) { return $this->get_link( '/sites/%d/media/%d', $blog_id, $media_id, $path ); } /** * Generate the site link endpoint URL * * Used to construct meta links in API responses, specific to /site links. * * @param int $blog_id The site's Jetpack blog ID. * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including site information. **/ public function get_site_link( $blog_id, $path = '' ) { return $this->get_link( '/sites/%d', $blog_id, $path ); } /** * Generate the posts endpoint URL * * Used to construct meta links in API responses, specific to posts links. * * @param int $blog_id The site's Jetpack blog ID. * @param int $post_id The post ID. * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including post information. **/ public function get_post_link( $blog_id, $post_id, $path = '' ) { return $this->get_link( '/sites/%d/posts/%d', $blog_id, $post_id, $path ); } /** * Generate the comments endpoint URL * * Used to construct meta links in API responses, specific to comments links. * * @param int $blog_id The site's Jetpack blog ID. * @param int $comment_id The comment ID. * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including comment information. **/ public function get_comment_link( $blog_id, $comment_id, $path = '' ) { return $this->get_link( '/sites/%d/comments/%d', $blog_id, $comment_id, $path ); } /** * Generate the endpoint URL for Publicize connections * * Used to construct meta links in API responses, specific to Publicize connections. * * @param int $blog_id The site's Jetpack blog ID. * @param int $publicize_connection_id The ID of the Publicize connection. * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including Publicize connection information. **/ public function get_publicize_connection_link( $blog_id, $publicize_connection_id, $path = '' ) { return $this->get_link( '.1/sites/%d/publicize-connections/%d', $blog_id, $publicize_connection_id, $path ); } /** * Generate the endpoint URL for a single Publicize connection including a Keyring connection * * Used to construct meta links in API responses, specific to a single Publicize and Keyring connection. * * @param int $keyring_token_id The ID of the Keyring connection. * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including specific Keyring connection information for a specific Publicize connection. **/ public function get_publicize_connections_link( $keyring_token_id, $path = '' ) { return $this->get_link( '.1/me/publicize-connections/?keyring_connection_ID=%d', $keyring_token_id, $path ); } /** * Generate the endpoint URL for a single Keyring connection * * Used to construct meta links in API responses, specific to a Keyring connections. * * @param int $keyring_token_id The ID of the Keyring connection. * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including specific Keyring connection. **/ public function get_keyring_connection_link( $keyring_token_id, $path = '' ) { return $this->get_link( '.1/me/keyring-connections/%d', $keyring_token_id, $path ); } /** * Generate the endpoint URL for an external service that can be integrated with via Keyring * * Used to construct meta links in API responses, specific to an external service. * * @param int $external_service The ID of the external service. * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including information about an external service that WordPress.com or Jetpack sites can integrate with via keyring. **/ public function get_external_service_link( $external_service, $path = '' ) { return $this->get_link( '.1/meta/external-services/%s', $external_service, $path ); } /** * Try to find the closest supported version of an endpoint to the current endpoint * * For example, if we were looking at the path /animals/panda: * - if the current endpoint is v1.3 and there is a v1.3 of /animals/%s available, we return 1.3 * - if the current endpoint is v1.3 and there is no v1.3 of /animals/%s known, we fall back to the * maximum available version of /animals/%s, e.g. 1.1 * * This method is used in get_link() to construct meta links for API responses. * * @param string $template_path The generic endpoint path, e.g. /sites/%s . * @param string $path The current endpoint path, relative to the version, e.g. /sites/12345 . * @param string $request_method Request method used to access the endpoint path . * @return string The current version, or otherwise the maximum version available */ public function get_closest_version_of_endpoint( $template_path, $path, $request_method = 'GET' ) { $closest_endpoint_cache_by_version = & $this->closest_endpoint_cache_by_version; $closest_endpoint_cache = & $closest_endpoint_cache_by_version[ $this->api->version ]; if ( ! $closest_endpoint_cache ) { $closest_endpoint_cache_by_version[ $this->api->version ] = array(); $closest_endpoint_cache = & $closest_endpoint_cache_by_version[ $this->api->version ]; } if ( ! isset( $closest_endpoint_cache[ $template_path ] ) ) { $closest_endpoint_cache[ $template_path ] = array(); } elseif ( isset( $closest_endpoint_cache[ $template_path ][ $request_method ] ) ) { return $closest_endpoint_cache[ $template_path ][ $request_method ]; } $path = untrailingslashit( $path ); // /help is a special case - always use the current request version if ( wp_endswith( $path, '/help' ) ) { $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version; return $this->api->version; } $matches_by_version = & $this->matches_by_version; // try to match out of saved matches. if ( ! isset( $matches_by_version[ $this->api->version ] ) ) { $matches_by_version[ $this->api->version ] = array(); } foreach ( $matches_by_version[ $this->api->version ] as $match ) { $regex = $match->regex; if ( preg_match( "#^$regex\$#", $path ) ) { $closest_endpoint_cache[ $template_path ][ $request_method ] = $match->version; return $match->version; } } $endpoint_path_versions = $this->get_endpoint_path_versions(); $last_path_segment = $this->get_last_segment_of_relative_path( $path ); $max_version_found = null; foreach ( $endpoint_path_versions as $endpoint_last_path_segment => $endpoints ) { // Does the last part of the path match the path key? (e.g. 'posts') // If the last part contains a placeholder (e.g. %s), we want to carry on. // phpcs:ignore Universal.Operators.StrictComparisons.LooseNotEqual if ( $last_path_segment != $endpoint_last_path_segment && ! strstr( $endpoint_last_path_segment, '%' ) ) { continue; } foreach ( $endpoints as $endpoint ) { // Does the request method match? if ( ! in_array( $request_method, $endpoint['request_methods'], true ) ) { continue; } $endpoint_path = untrailingslashit( $endpoint['path'] ); $endpoint_path_regex = str_replace( array( '%s', '%d' ), array( '([^/?&]+)', '(\d+)' ), $endpoint_path ); if ( ! preg_match( "#^$endpoint_path_regex\$#", $path ) ) { continue; } // Make sure the endpoint exists at the same version. if ( null !== $this->api->version && version_compare( $this->api->version, $endpoint['min_version'], '>=' ) && version_compare( $this->api->version, $endpoint['max_version'], '<=' ) ) { array_push( $matches_by_version[ $this->api->version ], (object) array( 'version' => $this->api->version, 'regex' => $endpoint_path_regex, ) ); $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version; return $this->api->version; } // If the endpoint doesn't exist at the same version, record the max version we found. if ( empty( $max_version_found ) || version_compare( $max_version_found['version'], $endpoint['max_version'], '<' ) ) { $max_version_found = array( 'version' => $endpoint['max_version'], 'regex' => $endpoint_path_regex, ); } } } // If the endpoint version is less than the requested endpoint version, return the max version found. if ( ! empty( $max_version_found ) ) { array_push( $matches_by_version[ $this->api->version ], (object) $max_version_found ); $closest_endpoint_cache[ $template_path ][ $request_method ] = $max_version_found['version']; return $max_version_found['version']; } // Otherwise, use the API version of the current request. return $this->api->version; } /** * Get an array of endpoint paths with their associated versions * * @return array Array of endpoint paths, min_versions and max_versions, keyed by last segment of path **/ protected function get_endpoint_path_versions() { if ( ! empty( $this->cache_result ) ) { return $this->cache_result; } /* * Create a map of endpoints and their min/max versions keyed by the last segment of the path (e.g. 'posts') * This reduces the search space when finding endpoint matches in get_closest_version_of_endpoint() */ $endpoint_path_versions = array(); foreach ( $this->api->endpoints as $key => $endpoint_objects ) { // @todo As with the todo in class.json-api.php, we need to determine if anything depends on this being serialized and hence unserialized, rather than e.g. JSON. // The key contains a serialized path, min_version and max_version. list( $path, $min_version, $max_version ) = unserialize( $key ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize -- Legacy, see serialization at class.json-api.php. // Grab the last component of the relative path to use as the top-level key. $last_path_segment = $this->get_last_segment_of_relative_path( $path ); $endpoint_path_versions[ $last_path_segment ][] = array( 'path' => $path, 'min_version' => $min_version, 'max_version' => $max_version, 'request_methods' => array_keys( $endpoint_objects ), ); } $this->cache_result = $endpoint_path_versions; return $endpoint_path_versions; } /** * Grab the last segment of a relative path * * @param string $path Path. * @return string Last path segment */ protected function get_last_segment_of_relative_path( $path ) { $path_parts = array_filter( explode( '/', $path ) ); if ( empty( $path_parts ) ) { return null; } return end( $path_parts ); } } class.json-api-post-jetpack.php 0000644 00000004253 14722051450 0012507 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * This class extends the SAL_Post class, providing the implementation for * functions that were declared in that SAL_Post class. * * @see WPCOM_JSON_API_Post_v1_1_Endpoint in class.wpcom-json-api-post-v1-1-endpoint.php for more context on * the functions implemented here. * * @package automattic/jetpack */ /** * Base class for Jetpack_Post. */ class Jetpack_Post extends SAL_Post { /** * Defines a default value for the like counts on a post, if this hasn't been defined yet. * * @return int Returns 0. **/ public function get_like_count() { return 0; } /** * Defines a default value for whether or not the current user likes this post, if this hasn't been defined yet. * * @return bool Returns false **/ public function is_liked() { return false; } /** * Defines a default value for whether or not the current user reblogged this post, if this hasn't been defined yet. * * @return bool Returns false **/ public function is_reblogged() { return false; } /** * Defines a default value for whether or not the current user is following this blog, if this hasn't been defined yet. * * @return bool Returns false **/ public function is_following() { return false; } /** * Defines the unique WordPress.com-wide representation of a post, if this hasn't been defined yet. * * @return string Returns an empty string **/ public function get_global_id() { return ''; } /** * Defines a default value for whether or not there is gelocation data for this post, if this hasn't been defined yet. * * @return bool Returns false **/ public function get_geo() { return false; } /** * Returns the avatar URL for a user, or an empty string if there isn't a valid avatar. * * @param string $email The user's email. * @param int $avatar_size The size of the avatar in pixels. * * @return string */ protected function get_avatar_url( $email, $avatar_size = 96 ) { $avatar_url = get_avatar_url( $email, array( 'size' => $avatar_size, ) ); if ( ! $avatar_url || is_wp_error( $avatar_url ) ) { return ''; } return $avatar_url; } } class.json-api-post-base.php 0000644 00000076301 14722051450 0012003 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * This class wraps a WP_Post and proxies any undefined attributes * and methods to the wrapped class. We need to do this because at present * the WP_Post class is marked as final (in 4.5 this will change, though it's * not clear if there will be a mechanism to retrieve from the DB into the over- * ridden class dynamically). * * @package automattic/jetpack */ use Automattic\Jetpack\Status; require_once __DIR__ . '/class.json-api-metadata.php'; require_once __DIR__ . '/class.json-api-date.php'; require_once ABSPATH . 'wp-admin/includes/post.php'; require_once ABSPATH . 'wp-includes/post.php'; /** * Base class for SAL_Post. */ abstract class SAL_Post { /** * A WP_Post instance. * * @var WP_Post */ public $post; /** * The post request context (for example 'edit' or 'display') * * @var string */ public $context; /** * A Jetpack_Site instance. * * @var Jetpack_Site */ public $site; /** * Constructor function * * @param Jetpack_Site $site A Jetpack_Site instance. * @param WP_Post $post A WP_Post instance. * @param string $context The post request context (for example 'edit' or 'display'). */ public function __construct( $site, $post, $context ) { $this->post = $post; $this->context = $context; $this->site = $site; } /** * Setting this WP_Post instance's key value * * @param string $key The post key to set. * @param string $value The value to set the post key to (for example filter, ID, post_status). */ public function __set( $key, $value ) { $this->post->{ $key } = $value; } /** * Returning a WPCOM_JSON_API_Links instance if the post key is set to 'links', or the post key value. * * @param string $key The post key value. * * @return WPCOM_JSON_API_Links|string */ public function __get( $key ) { if ( 'links' === $key ) { require_once __DIR__ . '/class.json-api-links.php'; return WPCOM_JSON_API_Links::getInstance(); } return $this->post->{ $key }; } /** * A function to either call a given function, or return an error if it doesn't exist. * * @param string $name A function name to be called. * @param mixed $arguments Arguments to be passed into the given function. * * @return mixed|bool */ public function __call( $name, $arguments ) { if ( is_callable( array( $this->post, $name ) ) ) { return call_user_func_array( array( $this->post, $name ), $arguments ); } else { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error trigger_error( esc_html( sprintf( /* translators: %s is the method name that has been called */ __( 'Call to undefined method %s', 'jetpack' ), $name ) ) ); } } /** * Checking to see if a given property is set. * * @param string $name Property to check if set. * * @return bool */ public function __isset( $name ) { return isset( $this->post->{ $name } ); } /** * Defining a base get_like_count() function to be extended in the Jetpack_Post class. * * This will define a default value for the like counts on a post, if this hasn't been defined yet. * * @see class.json-api-post-jetpack.php */ abstract public function get_like_count(); /** * Defining a base is_liked() function to be extended in the Jetpack_Post class. * * This will define a default value for whether or not the current user likes this post, if this hasn't been defined yet. * * @see class.json-api-post-jetpack.php */ abstract public function is_liked(); /** * Defining a base is_reblogged() function to be extended in the Jetpack_Post class. * * This will define a default value for whether or not the current user reblogged this post, if this hasn't been defined yet. * * @see class.json-api-post-jetpack.php */ abstract public function is_reblogged(); /** * Defining a base is_following() function to be extended in the Jetpack_Post class. * * This will define a default value for whether or not the current user is following this blog, if this hasn't been defined yet. * * @see class.json-api-post-jetpack.php */ abstract public function is_following(); /** * Defining a base get_global_id() function to be extended in the Jetpack_Post class. * * This will define the unique WordPress.com-wide representation of a post, if this hasn't been defined yet. * * @see class.json-api-post-jetpack.php */ abstract public function get_global_id(); /** * Defining a base get_geo() function to be extended in the Jetpack_Post class. * * This will define a default value for whether or not there is gelocation data for this post, if this hasn't been defined yet. * * @see class.json-api-post-jetpack.php */ abstract public function get_geo(); /** * Returns an int which helps define the menu order for the post. * * @return int */ public function get_menu_order() { return (int) $this->post->menu_order; } /** * Returns a string which represents the post's GUID. * * @return string */ public function get_guid() { return (string) $this->post->guid; } /** * Returns a string which represents the post type. * * @return string */ public function get_type() { return (string) $this->post->post_type; } /** * Returns an object which holds the terms associated with that post object. * * @return object */ public function get_terms() { $taxonomies = get_object_taxonomies( $this->post, 'objects' ); $terms = array(); foreach ( $taxonomies as $taxonomy ) { if ( ! $taxonomy->public && ! current_user_can( $taxonomy->cap->assign_terms ) ) { continue; } $terms[ $taxonomy->name ] = array(); $taxonomy_terms = wp_get_object_terms( $this->post->ID, $taxonomy->name, array( 'fields' => 'all' ) ); foreach ( $taxonomy_terms as $term ) { $formatted_term = $this->format_taxonomy( $term, $taxonomy->name, 'display' ); $terms[ $taxonomy->name ][ $term->name ] = $formatted_term; } $terms[ $taxonomy->name ] = (object) $terms[ $taxonomy->name ]; } return (object) $terms; } /** * Returns an object which holds the posts tag details * * @return object */ public function get_tags() { $tags = array(); $terms = wp_get_post_tags( $this->post->ID ); foreach ( $terms as $term ) { if ( ! empty( $term->name ) ) { $tags[ $term->name ] = $this->format_taxonomy( $term, 'post_tag', 'display' ); } } return (object) $tags; } /** * Returns an object which holds the posts category details * * @return object */ public function get_categories() { $categories = array(); $terms = wp_get_object_terms( $this->post->ID, 'category', array( 'fields' => 'all' ) ); foreach ( $terms as $term ) { if ( ! empty( $term->name ) ) { $categories[ $term->name ] = $this->format_taxonomy( $term, 'category', 'display' ); } } return (object) $categories; } /** * Returns an array of objects which hold the posts attachment information and numbers representing how many associated posts are found. * * @return array */ public function get_attachments_and_count() { $attachments = array(); $_attachments = new WP_Query( array( 'post_parent' => $this->post->ID, 'post_status' => 'inherit', 'post_type' => 'attachment', 'posts_per_page' => '20', ) ); foreach ( $_attachments->posts as $attachment ) { $attachments[ $attachment->ID ] = $this->get_media_item_v1_1( $attachment->ID ); } return array( (object) $attachments, (int) $_attachments->found_posts ); } /** * Returns an array with a posts metadata information. * * @return array */ public function get_metadata() { $metadata = array(); foreach ( (array) has_meta( $this->post->ID ) as $meta ) { // Don't expose protected fields. $meta_key = $meta['meta_key']; $show = ! ( WPCOM_JSON_API_Metadata::is_internal_only( $meta_key ) ) && ( WPCOM_JSON_API_Metadata::is_public( $meta_key ) || current_user_can( 'edit_post_meta', $this->post->ID, $meta_key ) ); if ( Jetpack_SEO_Posts::DESCRIPTION_META_KEY === $meta_key && ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) { $show = false; } if ( $show ) { $metadata[] = array( 'id' => $meta['meta_id'], 'key' => $meta['meta_key'], 'value' => $this->safe_maybe_unserialize( $meta['meta_value'] ), ); } } return $metadata; } /** * Returns an object with a posts link meta details. * * @return object */ public function get_meta() { $meta = (object) array( 'links' => (object) array( 'self' => (string) $this->get_post_link(), 'help' => (string) $this->get_post_link( 'help' ), 'site' => (string) $this->get_site_link(), 'replies' => (string) $this->get_post_link( 'replies/' ), 'likes' => (string) $this->get_post_link( 'likes/' ), ), ); $amp_permalink = get_post_meta( $this->post->ID, '_jetpack_amp_permalink', true ); if ( ! empty( $amp_permalink ) ) { $meta->links->amp = (string) $amp_permalink; } // add autosave link if a more recent autosave exists. if ( 'edit' === $this->context ) { $autosave = wp_get_post_autosave( $this->post->ID ); if ( $autosave && $autosave->post_modified > $this->post->post_modified ) { $meta->links->autosave = (string) $this->get_post_link() . '/autosave'; } } return $meta; } /** * Returns an array with the current user's publish, deletion and edit capabilities. * * @return array */ public function get_current_user_capabilities() { return array( 'publish_post' => current_user_can( 'publish_post', $this->post->ID ), 'delete_post' => current_user_can( 'delete_post', $this->post->ID ), 'edit_post' => current_user_can( 'edit_post', $this->post->ID ), ); } /** * Returns an array with post revision ids, or false if 'edit' isn't the current post request context. * * @return bool|array */ public function get_revisions() { if ( 'edit' !== $this->context ) { return false; } $args = array( 'posts_per_page' => -1, 'post_type' => 'revision', 'post_status' => 'any', 'fields' => 'ids', // Fetch only the IDs. 'post_parent' => $this->post->ID, ); $revision_query = new WP_Query( $args ); return $revision_query->posts; // This returns an array of revision IDs. } /** * Returns an object with extra post permalink suggestions. * * @return object */ public function get_other_urls() { $other_urls = array(); if ( 'publish' !== $this->post->post_status ) { $other_urls = $this->get_permalink_suggestions( $this->post->post_title ); } return (object) $other_urls; } /** * Calls the WPCOM_JSON_API_Links get_site_link() function to generate a site link endpoint URL. * * @return string Endpoint URL including site information. */ protected function get_site_link() { return $this->links->get_site_link( $this->site->get_id() ); } /** * Calls the WPCOM_JSON_API_Links get_post_link() function to generate a posts endpoint URL. * * @param string $path Optional path to be appended to the URL. * @return string Endpoint URL including post information. */ protected function get_post_link( $path = null ) { return $this->links->get_post_link( $this->site->get_id(), $this->post->ID, $path ); } /** * Returns an array of user and post specific social media post URLs. * * @return array */ public function get_publicize_urls() { $publicize_urls = array(); $publicize = get_post_meta( $this->post->ID, 'publicize_results', true ); if ( $publicize ) { foreach ( $publicize as $service => $data ) { switch ( $service ) { // @todo explore removing once Twitter is removed from Publicize. case 'twitter': foreach ( $data as $datum ) { $publicize_urls[] = esc_url_raw( "https://twitter.com/{$datum['user_id']}/status/{$datum['post_id']}" ); } break; case 'fb': foreach ( $data as $datum ) { $publicize_urls[] = esc_url_raw( "https://www.facebook.com/permalink.php?story_fbid={$datum['post_id']}&id={$datum['user_id']}" ); } break; } } } return (array) $publicize_urls; } /** * Returns a string with the page's custom template metadata. * * @return string */ public function get_page_template() { return (string) get_post_meta( $this->post->ID, '_wp_page_template', true ); } /** * Returns a string representing the source URL of a post's featured image (or an empty string otherwise). * * Note - this is overridden in jetpack-shadow * * @return string */ public function get_featured_image() { $image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( $this->post->ID ), 'full' ); if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) { return (string) $image_attributes[0]; } else { return ''; } } /** * Returns an object representing a post's featured image thumbnail image. * * @return object */ public function get_post_thumbnail() { $thumb = null; $thumb_id = get_post_thumbnail_id( $this->post->ID ); if ( ! empty( $thumb_id ) ) { $attachment = get_post( $thumb_id ); if ( ! empty( $attachment ) ) { $featured_image_object = $this->get_attachment( $attachment ); } if ( ! empty( $featured_image_object ) ) { $thumb = (object) $featured_image_object; } } return $thumb; } /** * Returns the format slug for a post (for example 'link', 'image' - the default being 'standard'). * * @return string */ public function get_format() { $format = (string) get_post_format( $this->post->ID ); if ( ! $format ) { $format = 'standard'; } return $format; } /** * Returns an object with the post's attachment details. * * @param WP_POST $attachment The post's attachment details in the form of a WP_POST object. * * @return object */ private function get_attachment( $attachment ) { $metadata = wp_get_attachment_metadata( $attachment->ID ); $result = array( 'ID' => (int) $attachment->ID, 'URL' => (string) wp_get_attachment_url( $attachment->ID ), 'guid' => (string) $attachment->guid, 'mime_type' => (string) $attachment->post_mime_type, 'width' => (int) isset( $metadata['width'] ) ? $metadata['width'] : 0, 'height' => (int) isset( $metadata['height'] ) ? $metadata['height'] : 0, ); if ( isset( $metadata['duration'] ) ) { $result['duration'] = (int) $metadata['duration']; } /** This filter is documented in class.jetpack-sync.php */ return (object) apply_filters( 'get_attachment', $result ); } /** * Returns an ISO 8601 formatted datetime string representing the date of post creation. * * @return string */ public function get_date() { return (string) WPCOM_JSON_API_Date::format_date( $this->post->post_date_gmt, $this->post->post_date ); } /** * Returns an ISO 8601 formatted datetime string representing the date the post was last modified. * * @return string */ public function get_modified_date() { return (string) WPCOM_JSON_API_Date::format_date( $this->post->post_modified_gmt, $this->post->post_modified ); } /** * Returns the post's title. * * @return string */ public function get_title() { if ( 'display' === $this->context ) { return (string) get_the_title( $this->post->ID ); } else { return (string) htmlspecialchars_decode( $this->post->post_title, ENT_QUOTES ); } } /** * Returns the permalink for the post (or the post parent if the post type is a revision). * * @return string */ public function get_url() { if ( 'revision' === $this->post->post_type ) { return (string) esc_url_raw( get_permalink( $this->post->post_parent ) ); } else { return (string) esc_url_raw( get_permalink( $this->post->ID ) ); } } /** * Returns the shortlink for the post. * * @return string */ public function get_shortlink() { return (string) esc_url_raw( wp_get_shortlink( $this->post->ID ) ); } /** * Returns the post content, or a string saying 'This post is password protected' if that is the case. * * @return string */ public function get_content() { if ( 'display' === $this->context ) { // @todo: move this WPCOM-specific hack add_filter( 'the_password_form', array( $this, 'the_password_form' ) ); $content = (string) $this->get_the_post_content_for_display(); remove_filter( 'the_password_form', array( $this, 'the_password_form' ) ); return $content; } else { return (string) $this->post->post_content; } } /** * Returns the post excerpt, or a string saying 'This post is password protected' if that is the case. * * @return string */ public function get_excerpt() { if ( 'display' === $this->context ) { add_filter( 'the_password_form', array( $this, 'the_password_form' ) ); ob_start(); the_excerpt(); $response = (string) ob_get_clean(); remove_filter( 'the_password_form', array( $this, 'the_password_form' ) ); } else { $response = htmlspecialchars_decode( (string) $this->post->post_excerpt, ENT_QUOTES ); } return $response; } /** * Returns the current post status (publish, future, draft, pending, private). * * @return string */ public function get_status() { return (string) get_post_status( $this->post->ID ); } /** * Returns true if the post is a sticky post, false otherwise. * * @return bool */ public function is_sticky() { return (bool) is_sticky( $this->post->ID ); } /** * Returns the post's slug. * * @return string */ public function get_slug() { return (string) $this->post->post_name; } /** * Returns the post's password, if password protected. * * @return string */ public function get_password() { $password = (string) $this->post->post_password; if ( 'edit' === $this->context ) { $password = htmlspecialchars_decode( (string) $password, ENT_QUOTES ); } return $password; } /** * Returns an object representing a post's parent, and false if it doesn't have one. * * @return object|bool */ public function get_parent() { if ( $this->post->post_parent ) { $parent = get_post( $this->post->post_parent ); if ( 'display' === $this->context ) { $parent_title = (string) get_the_title( $parent->ID ); } else { $parent_title = (string) htmlspecialchars_decode( $this->post->post_title, ENT_QUOTES ); } return (object) array( 'ID' => (int) $parent->ID, 'type' => (string) $parent->post_type, 'link' => (string) $this->links->get_post_link( $this->site->get_id(), $parent->ID ), 'title' => $parent_title, ); } else { return false; } } /** * Returns a string saying 'This post is password protected' (to be later used within the_password_form filter). * * @return string */ public function the_password_form() { return __( 'This post is password protected.', 'jetpack' ); } /** * Returns an array with information related to the comment and ping status of a post. * * @return array */ public function get_discussion() { return array( 'comments_open' => (bool) comments_open( $this->post->ID ), 'comment_status' => (string) $this->post->comment_status, 'pings_open' => (bool) pings_open( $this->post->ID ), 'ping_status' => (string) $this->post->ping_status, 'comment_count' => (int) $this->post->comment_count, ); } /** * Returns true if likes are enabled - either for the post, or site-wide. * * @return bool */ public function is_likes_enabled() { /** This filter is documented in modules/likes.php */ $sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) ); $post_likes_switched = get_post_meta( $this->post->ID, 'switch_like_status', true ); return $post_likes_switched || ( $sitewide_likes_enabled && '0' !== $post_likes_switched ); } /** * Returns true if sharing is enabled, false otherwise. * * @return bool */ public function is_sharing_enabled() { $show = true; /** This filter is documented in modules/sharedaddy/sharing-service.php */ $show = apply_filters( 'sharing_show', $show, $this->post ); $switched_status = get_post_meta( $this->post->ID, 'sharing_disabled', false ); if ( ! empty( $switched_status ) ) { $show = false; } return (bool) $show; } /** * Returns the post content in the form of a string, ready for displaying. * * Note: No Blog ID parameter. No Post ID parameter. Depends on globals. * Expects setup_postdata() to already have been run * * @return string */ public function get_the_post_content_for_display() { global $pages, $page; $old_pages = $pages; $old_page = $page; $content = implode( "\n\n", $pages ); $content = preg_replace( '/<!--more(.*?)?-->/', '', $content ); // phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited -- Assignment to globals is intentional $pages = array( $content ); $page = 1; ob_start(); the_content(); $return = ob_get_clean(); $pages = $old_pages; $page = $old_page; // phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited return $return; } /** * Returns an object containing the post author's information (eg. ID, display name, email if the user has post editing capabilities). * * @return object */ public function get_author() { if ( 0 == $this->post->post_author ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- numbers could be numeric strings. return null; } $show_email = 'edit' === $this->context && current_user_can( 'edit_post', $this->post->ID ); $user = get_user_by( 'id', $this->post->post_author ); if ( ! $user || is_wp_error( $user ) ) { return null; } // @todo: factor this out // phpcs:disable WordPress.NamingConventions.ValidVariableName if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { $active_blog = get_active_blog_for_user( $user->ID ); $site_id = $active_blog->blog_id ?? -1; $profile_URL = "https://gravatar.com/{$user->user_login}"; } else { $profile_URL = 'https://gravatar.com/' . md5( strtolower( trim( $user->user_email ) ) ); $site_id = -1; } $author = array( 'ID' => (int) $user->ID, 'login' => (string) $user->user_login, 'email' => $show_email ? (string) $user->user_email : false, 'name' => (string) $user->display_name, 'first_name' => (string) $user->first_name, 'last_name' => (string) $user->last_name, 'nice_name' => (string) $user->user_nicename, 'URL' => (string) esc_url_raw( $user->user_url ), 'avatar_URL' => (string) esc_url_raw( $this->get_avatar_url( $user->user_email ) ), 'profile_URL' => (string) esc_url_raw( $profile_URL ), ); // phpcs:enable WordPress.NamingConventions.ValidVariableName if ( $site_id > -1 ) { $author['site_ID'] = (int) $site_id; } return (object) $author; } /** * Returns the avatar URL for a user, or an empty string if there isn't a valid avatar. * * @param string $email The user's email. * @param int $avatar_size The size of the avatar in pixels. * * @todo Provide a non-WP.com option. * * @return string */ protected function get_avatar_url( $email, $avatar_size = 96 ) { $avatar_url = function_exists( 'wpcom_get_avatar_url' ) ? wpcom_get_avatar_url( $email, $avatar_size ) : ''; if ( ! $avatar_url || is_wp_error( $avatar_url ) ) { return ''; } return esc_url_raw( htmlspecialchars_decode( $avatar_url[0], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ) ); } /** * Return extra post permalink suggestions in an array including the 'permalink_URL' and the 'suggested_slug'. * * @param string $title The current post title. * * @return array */ public function get_permalink_suggestions( $title ) { $suggestions = array(); list( $suggestions['permalink_URL'], $suggestions['suggested_slug'] ) = get_sample_permalink( $this->post->ID, $title ); return $suggestions; } /** * Returns an object with formatted taxonomy information such as slug and meta information. * * Otherwise, returns an error if the edit or display permissions aren't correct. * * @param WP_Term $taxonomy The current taxonomy. * @param string $taxonomy_type The current taxonomy type, for example 'category'. * @param string $context The current context, for example 'edit' or 'display'. * * @return object */ private function format_taxonomy( $taxonomy, $taxonomy_type, $context ) { // Permissions. switch ( $context ) { case 'edit': $tax = get_taxonomy( $taxonomy_type ); if ( ! current_user_can( $tax->cap->edit_terms ) ) { return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 ); } break; case 'display': if ( ( new Status() )->is_private_site() && ! current_user_can( 'read' ) ) { return new WP_Error( 'unauthorized', 'User cannot view taxonomy', 403 ); } break; default: return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 ); } $response = array(); $response['ID'] = (int) $taxonomy->term_id; $response['name'] = (string) $taxonomy->name; $response['slug'] = (string) $taxonomy->slug; $response['description'] = (string) $taxonomy->description; $response['post_count'] = (int) $taxonomy->count; if ( is_taxonomy_hierarchical( $taxonomy_type ) ) { $response['parent'] = (int) $taxonomy->parent; } $response['meta'] = (object) array( 'links' => (object) array( 'self' => (string) $this->links->get_taxonomy_link( $this->site->get_id(), $taxonomy->slug, $taxonomy_type ), 'help' => (string) $this->links->get_taxonomy_link( $this->site->get_id(), $taxonomy->slug, $taxonomy_type, 'help' ), 'site' => (string) $this->links->get_site_link( $this->site->get_id() ), ), ); return (object) $response; } /** * Builds and returns the media item's details. * * @param int $media_id The media item ID. * @todo: factor this out into site. * * @return object */ private function get_media_item_v1_1( $media_id ) { $media_item = get_post( $media_id ); if ( ! $media_item || is_wp_error( $media_item ) ) { return new WP_Error( 'unknown_media', 'Unknown Media', 404 ); } $file = basename( wp_get_attachment_url( $media_item->ID ) ); $file_info = pathinfo( $file ); $ext = isset( $file_info['extension'] ) ? $file_info['extension'] : ''; $response = array( 'ID' => $media_item->ID, 'URL' => wp_get_attachment_url( $media_item->ID ), 'guid' => $media_item->guid, 'date' => (string) WPCOM_JSON_API_Date::format_date( $media_item->post_date_gmt, $media_item->post_date ), 'post_ID' => $media_item->post_parent, 'author_ID' => (int) $media_item->post_author, 'file' => $file, 'mime_type' => $media_item->post_mime_type, 'extension' => $ext, 'title' => $media_item->post_title, 'caption' => $media_item->post_excerpt, 'description' => $media_item->post_content, 'alt' => get_post_meta( $media_item->ID, '_wp_attachment_image_alt', true ), 'thumbnails' => array(), ); if ( in_array( $ext, array( 'jpg', 'jpeg', 'png', 'gif', 'webp' ), true ) ) { $metadata = wp_get_attachment_metadata( $media_item->ID ); if ( isset( $metadata['height'] ) && isset( $metadata['width'] ) ) { $response['height'] = $metadata['height']; $response['width'] = $metadata['width']; } if ( isset( $metadata['sizes'] ) ) { /** * Filter the thumbnail sizes available for each attachment ID. * * @module json-api * * @since 3.9.0 * * @param array $metadata['sizes'] Array of thumbnail sizes available for a given attachment ID. * @param int $media_id The media item ID. */ $sizes = apply_filters( 'rest_api_thumbnail_sizes', $metadata['sizes'], $media_id ); if ( is_array( $sizes ) ) { foreach ( $sizes as $size => $size_details ) { $response['thumbnails'][ $size ] = dirname( $response['URL'] ) . '/' . $size_details['file']; } } } if ( isset( $metadata['image_meta'] ) ) { $response['exif'] = $metadata['image_meta']; } } if ( in_array( $ext, array( 'mp3', 'm4a', 'wav', 'ogg' ), true ) ) { $metadata = wp_get_attachment_metadata( $media_item->ID ); if ( isset( $metadata['length'] ) ) { $response['length'] = $metadata['length']; } $response['exif'] = $metadata; } if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ), true ) ) { $metadata = wp_get_attachment_metadata( $media_item->ID ); if ( isset( $metadata['height'] ) && isset( $metadata['width'] ) ) { $response['height'] = $metadata['height']; $response['width'] = $metadata['width']; } if ( isset( $metadata['length'] ) ) { $response['length'] = $metadata['length']; } if ( empty( $response['length'] ) && isset( $metadata['duration'] ) ) { $response['length'] = (int) $metadata['duration']; } if ( empty( $response['length'] ) && isset( $metadata['videopress']['duration'] ) ) { $response['length'] = ceil( $metadata['videopress']['duration'] / 1000 ); } // add VideoPress info. if ( function_exists( 'video_get_info_by_blogpostid' ) ) { $info = video_get_info_by_blogpostid( $this->site->get_id(), $media_id ); // Thumbnails. if ( function_exists( 'video_format_done' ) && function_exists( 'video_image_url_by_guid' ) ) { $response['thumbnails'] = array( 'fmt_hd' => '', 'fmt_dvd' => '', 'fmt_std' => '', ); foreach ( $response['thumbnails'] as $size => $thumbnail_url ) { if ( video_format_done( $info, $size ) ) { $response['thumbnails'][ $size ] = video_image_url_by_guid( $info->guid, $size ); } else { unset( $response['thumbnails'][ $size ] ); } } } $response['videopress_guid'] = $info->guid ?? null; $response['videopress_processing_done'] = true; $response['videopress_processing_done'] = isset( $info->finish_date_gmt ) && '0000-00-00 00:00:00' !== $info->finish_date_gmt ? $info->finish_date_gmt : false; } } $response['thumbnails'] = (object) $response['thumbnails']; $response['meta'] = (object) array( 'links' => (object) array( 'self' => (string) $this->links->get_media_link( $this->site->get_id(), $media_id ), 'help' => (string) $this->links->get_media_link( $this->site->get_id(), $media_id, 'help' ), 'site' => (string) $this->links->get_site_link( $this->site->get_id() ), ), ); // add VideoPress link to the meta. if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ), true ) ) { if ( function_exists( 'video_get_info_by_blogpostid' ) ) { $response['meta']->links->videopress = (string) $this->links->get_link( '/videos/%s', $response['videopress_guid'], '' ); } } if ( $media_item->post_parent > 0 ) { $response['meta']->links->parent = (string) $this->links->get_post_link( $this->site->get_id(), $media_item->post_parent ); } return (object) $response; } /** * Temporary wrapper around maybe_unserialize() to catch exceptions thrown by unserialize(). * * Can be removed after https://core.trac.wordpress.org/ticket/45895 lands in Core. * * @param string $original Serialized string. * * @return string Unserialized string or original string if an exception was raised. **/ protected function safe_maybe_unserialize( $original ) { try { return maybe_unserialize( $original ); } catch ( Exception $e ) { return $original; } } } class.json-api-date.php 0000644 00000004767 14722051450 0011032 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * WPCOM_JSON_API_Date class. * * @package automattic/jetpack */ /** * Base class for WPCOM_JSON_API_Date. */ class WPCOM_JSON_API_Date { /** * Returns ISO 8601 formatted datetime: 2011-12-08T01:15:36-08:00 * * @param string $date_gmt GMT datetime string. * @param string $date Optional. Used to calculate the offset from GMT. * * @return string */ public static function format_date( $date_gmt, $date = null ) { $offset = null; $timestamp_gmt = strtotime( "$date_gmt+0000" ); if ( null === $date ) { $timestamp = $timestamp_gmt; $west = 0; $minutes = 0; $hours = 0; } else { $date_time = date_create( "$date+0000" ); if ( $date_time ) { $timestamp = date_format( $date_time, 'U' ); } else { $timestamp = 0; } // "0000-00-00 00:00:00" == -62169984000 if ( -62169984000 === $timestamp_gmt ) { // WordPress sets post_date=now, post_date_gmt="0000-00-00 00:00:00" for all drafts // WordPress sets post_modified=now, post_modified_gmt="0000-00-00 00:00:00" for new drafts. // Try to guess the correct offset from the blog's options. $timezone_string = get_option( 'timezone_string' ); if ( $timezone_string && $date_time ) { $timezone = timezone_open( $timezone_string ); if ( $timezone ) { $offset = $timezone->getOffset( $date_time ); } } else { $offset = 3600 * get_option( 'gmt_offset' ); } } else { $offset = $timestamp - $timestamp_gmt; } $west = $offset < 0; $offset = abs( $offset ); $hours = (int) floor( $offset / 3600 ); $offset -= $hours * 3600; $minutes = (int) floor( $offset / 60 ); } return (string) gmdate( 'Y-m-d\\TH:i:s', $timestamp ) . sprintf( '%s%02d:%02d', $west ? '-' : '+', $hours, $minutes ); } /** * Returns ISO 8601 formatted duration interval: P0DT1H10M0S * * @param string $time Duration in minutes or hours. * * @return null|string */ public static function format_duration( $time ) { $timestamp = strtotime( $time, 0 ); // Bail early if we don't recognize a date. if ( empty( $timestamp ) ) { return; } $days = floor( $timestamp / 86400 ); $timestamp = $timestamp % 86400; $hours = floor( $timestamp / 3600 ); $timestamp = $timestamp % 3600; $minutes = floor( $timestamp / 60 ); $timestamp = $timestamp % 60; return (string) sprintf( 'P%dDT%dH%dM%dS', $days, $hours, $minutes, $timestamp ); } } class.json-api-platform-jetpack.php 0000644 00000002473 14722051450 0013350 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * WPORG_Platform class that extends SAL_Platform, returning a Jetpack_Site with a $blog_id and $token * * @package automattic/jetpack */ require_once __DIR__ . '/class.json-api-platform.php'; /** * Base class for WPORG_Platform, which extends SAL_Platform */ class WPORG_Platform extends SAL_Platform { /** * Given a Jetpack blog ID, this function returns a Jetpack_Site instance * * @param int $blog_id A Jetpack blog ID. * @return Jetpack_Site A Jetpack_Site instance including all relevant details needed to define a Jetpack site. **/ public function get_site( $blog_id ) { require_once __DIR__ . '/class.json-api-site-jetpack.php'; return new Jetpack_Site( $blog_id, $this ); } } // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move these functions to some other file. /** * Given a token instance (with blog and user id related information), this function returns a new WPORG_Platform instance * * @param SAL_Token $token A token instance. * @see class.json-api-token.php * @return WPORG_Platform A WPORG_Platform instance including all relevant details needed to define a Jetpack site, as well as a token instance. **/ function wpcom_get_sal_platform( $token ) { return new WPORG_Platform( $token ); } class.json-api-site-base.php 0000644 00000125577 14722051450 0011774 0 ustar 00 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * This file defines the base class for the Site Abstraction Layer (SAL). * Note that this is the site "as seen by user $user_id with token $token", which * is why we pass the token to the platform; these site instances are value objects * to be used in the context of a single request for a single user. * Also note that at present this class _assumes_ you've "switched to" * the site in question, and functions like `get_bloginfo( 'name' )` will * therefore return the correct value. * * @package automattic/jetpack **/ use Automattic\Jetpack\Blaze; use Automattic\Jetpack\Status; use Automattic\Jetpack\Status\Host; require_once __DIR__ . '/class.json-api-date.php'; require_once __DIR__ . '/class.json-api-post-base.php'; /** * Base class for SAL_Site. * The abstract functions here are extended by Abstract_Jetpack_Site in class.json-api-site-jetpack-base.php. */ abstract class SAL_Site { /** * The Jetpack blog ID for the site. * * @var int */ public $blog_id; /** * A new WPORG_Platform instance. * * @see class.json-api-platform-jetpack.php. * * @var WPORG_Platform */ public $platform; /** * Contructs the SAL_Site instance. * * @param int $blog_id The Jetpack blog ID for the site. * @param WPORG_Platform $platform A new WPORG_Platform instance. */ public function __construct( $blog_id, $platform ) { $this->blog_id = $blog_id; $this->platform = $platform; } /** * Get the blog_id property. * * @return int */ public function get_id() { return $this->blog_id; } /** * Returns the site name. * * @return string */ public function get_name() { return (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); } /** * Returns the site description. * * @return string */ public function get_description() { return (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES ); } /** * Returns the URL for the current site. * * @return string */ public function get_url() { return (string) home_url(); } /** * Returns the number of published posts with the 'post' post-type. * * @return int */ public function get_post_count() { return (int) wp_count_posts( 'post' )->publish; } /** * A prototype function for get_quota - currently returns null. * * @return null */ public function get_quota() { return null; } /** * Returns an array of blogging prompt settings. Only applicable on WordPress.com. * * Data comes from .com since the fearture requires a .com connection to work. * * @param int $user_id the current user_id. * @param int $blog_id the blog id in this context. */ public function get_blogging_prompts_settings( $user_id, $blog_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable return false; } /** * Returns true if a site has the 'videopress' option enabled, false otherwise. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function has_videopress(); /** * Returns VideoPress storage used, in MB. * * @see class.json-api-site-jetpack-shadow.php on WordPress.com for implementation. Only applicable on WordPress.com. */ abstract public function get_videopress_storage_used(); /** * Sets the upgraded_filetypes_enabled Jetpack option to true as a default. Only relevant for WordPress.com sites. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function upgraded_filetypes_enabled(); /** * Sets the is_mapped_domain Jetpack option to true as a default. * * Primarily used in WordPress.com to confirm the current blog's domain does or doesn't match the primary redirect. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_mapped_domain(); /** * Fallback to the home URL since all Jetpack sites don't have an unmapped *.wordpress.com domain. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_unmapped_url(); /** * Whether the domain is a site redirect or not. Defaults to false on a Jetpack site. * * 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. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_redirect(); /** * 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. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_headstart_fresh(); /** * If the site's current theme supports post thumbnails, return true (otherwise return false). * * @see class.json-api-site-jetpack-base.php for implementation. */ abstract public function featured_images_enabled(); /** * Whether or not the Jetpack 'wordads' module is active on the site. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function has_wordads(); /** * Defaults to false on Jetpack sites, however is used on WordPress.com sites. This nonce is used for previews on Jetpack sites. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_frame_nonce(); /** * Defaults to false on Jetpack sites, however is used on WordPress.com sites where * it creates a nonce to be used with iframed block editor requests to a Jetpack site. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_jetpack_frame_nonce(); /** * Returns the allowed mime types and file extensions for a site. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function allowed_file_types(); /** * Returns an array of supported post formats. * * @see class.json-api-site-jetpack-base.php for implementation. */ abstract public function get_post_formats(); /** * Return site's privacy status. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_private(); /** * Return site's coming soon status. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_coming_soon(); /** * Whether or not the current user is following this blog. Defaults to false. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_following(); /** * Defaults to 0 for the number of WordPress.com subscribers - this is filled in on the WordPress.com side. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_subscribers_count(); /** * Returns the language code for the current site. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_locale(); /** * The flag indicates that the site has Jetpack installed. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_jetpack(); /** * The flag indicates that the site is connected to WP.com via Jetpack Connection. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_jetpack_connection(); /** * This function returns the values of any active Jetpack modules. * * @see class.json-api-site-jetpack-base.php for implementation. */ abstract public function get_jetpack_modules(); /** * This function returns true if a specified Jetpack module is active, false otherwise. * * @see class.json-api-site-jetpack-base.php for implementation. * * @param string $module The Jetpack module name to check. */ abstract public function is_module_active( $module ); /** * This function returns false for a check as to whether a site is a VIP site or not. * * @see class.json-api-site-jetpack-base.php for implementation. */ abstract public function is_vip(); /** * Returns true if Multisite is enabled, false otherwise. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_multisite(); /** * Points to the user ID of the site owner * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_site_owner(); /** * Returns true if the current site is a single user site, false otherwise. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_single_user_site(); /** * Defaults to false instead of returning the current site plan. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_plan(); /** * Empty function declaration - this function is filled out on the WordPress.com side, returning true if the site has an AK / VP bundle. * * @see class.json-api-site-jetpack.php and /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. */ abstract public function get_ak_vp_bundle_enabled(); /** * Returns null for Jetpack sites. For WordPress.com sites this returns the value of the 'podcasting_archive' option. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_podcasting_archive(); /** * Return the last engine used for an import on the site. Not used in Jetpack. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_import_engine(); /** * Returns the front page meta description for current site. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_jetpack_seo_front_page_description(); /** * Returns custom title formats from site option. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_jetpack_seo_title_formats(); /** * Returns website verification codes. Allowed keys include: google, pinterest, bing, yandex, facebook. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_verification_services_codes(); /** * This function is implemented on WPCom sites, where a filter is removed which forces the URL to http. * * @see class.json-api-site-jetpack-base.php and /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. */ abstract public function before_render(); /** * If a user has manage options permissions and the site is the main site of the network, make updates visible. * * Called after response_keys have been rendered, which itself is used to return all the necessary information for a site’s response. * * @see class.json-api-site-jetpack-base.php for implementation. * * @param array $response an array of the response keys. */ abstract public function after_render( &$response ); /** * Extends the Jetpack options array with details including site constraints, WordPress and Jetpack versions, and plugins using the Jetpack connection. * * @see class.json-api-site-jetpack-base.php for implementation. * @todo factor this out? Seems an odd thing to have on a site * * @param array $options an array of the Jetpack options. */ abstract public function after_render_options( &$options ); /** * Wrap a WP_Post object with SAL methods, returning a Jetpack_Post object. * * @see class.json-api-site-jetpack.php for implementation. * * @param WP_Post $post A WP_Post object. * @param string $context The post request context (for example 'edit' or 'display'). */ abstract public function wrap_post( $post, $context ); /** * For Jetpack sites this will always return false. * * @see class.json-api-site-jetpack-base.php for implementation. * * @param int $post_id The post id. */ abstract protected function is_a8c_publication( $post_id ); /** * Return the user interactions with a site. Not used in Jetpack. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_user_interactions(); /** * Flag a site as deleted. Not used in Jetpack. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_deleted(); /** * Indicates that a site is an A4A client. Not used in Jetpack. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_a4a_client(); /** * Indicates that a site is an A4A dev site. * * @return bool */ public function is_a4a_dev_site() { if ( function_exists( 'has_blog_sticker' ) ) { return has_blog_sticker( 'a4a-is-dev-site' ); } return false; } /** * Return the user interactions with a site. Not used in Jetpack. * * @param string $role The capability to check. * @return bool * @see class.json-api-site-jetpack.php for implementation. * @see class.json-api-site-wpcom.php (on WPCOM) for Simple-site implementation. * @see class.json-api-site-jetpack-shadow.php (on WPCOM) for Atomic-site implementation. */ abstract public function current_user_can( $role ); /** * Defines a filter to set whether a site is an automated_transfer site or not. * * Default is false. * * @return bool */ public function is_automated_transfer() { /** * Filter if a site is an automated-transfer site. * * @module json-api * * @since 6.4.0 * * @param bool is_automated_transfer( $this->blog_id ) * @param int $blog_id Blog identifier. */ return apply_filters( 'jetpack_site_automated_transfer', false, $this->blog_id ); } /** * 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. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_wpforteams_site(); /** * Get hub blog id for P2 sites. * * @return null */ public function get_p2_hub_blog_id() { return null; } /** * Getter for the p2 organization ID. * * @return int */ public function get_p2_organization_id() { return 0; // WPForTeams\Constants\NO_ORG_ID not loaded. } /** * Get details used to render a thumbnail of the site. P2020 themed sites only. * * @return ?array */ public function get_p2_thumbnail_elements() { return null; } /** * Detect whether a site is a WordPress.com on Atomic site. * * @return bool */ public function is_wpcom_atomic() { return ( new Host() )->is_woa_site(); } /** * Detect whether a site is an automated transfer site and WooCommerce is active. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. * * @return bool - False for Jetpack-connected sites. */ public function is_wpcom_store() { return false; } /** * Indicate whether this site was ever a specific trial. * * @param string $trial The trial type to check for. * * @return bool */ public function was_trial( $trial ) { if ( function_exists( 'has_blog_sticker' ) ) { return has_blog_sticker( "had-{$trial}-trial" ); } return false; } /** * Indicate whether this site was upgraded from a trial plan at some point. * * @return bool */ public function was_upgraded_from_trial() { if ( function_exists( 'has_blog_sticker' ) ) { return has_blog_sticker( 'has-upgraded-from-ecommerce-trial' ); } return false; } /** * Detect whether a site has the WooCommerce plugin active. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. * * @return bool - Default false for Jetpack-connected sites. */ public function woocommerce_is_active() { return false; } /** * Whether the Editing Toolkit plugin is active (relevant only on WordPress.com). * * @return true */ public function editing_toolkit_is_active() { return true; } /** * Detect whether a site has access to the Jetpack cloud. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. * * @return bool - Default false for Jetpack-connected sites. */ public function is_cloud_eligible() { return false; } /** * Returns an array of WPCOM_Store products. * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php. * * @return bool - Default empty array for Jetpack-connected sites. */ public function get_products() { return array(); } /** * Get post by ID * * @param int $post_id The ID of the post. * @param string $context The context by which the post data is required (display or edit). * * @return Jetpack_Post Post object on success, WP_Error object on failure **/ public function get_post_by_id( $post_id, $context ) { $post = get_post( $post_id, OBJECT, $context ); if ( ! $post ) { return new WP_Error( 'unknown_post', 'Unknown post', 404 ); } $wrapped_post = $this->wrap_post( $post, $context ); // validate access return $this->validate_access( $wrapped_post ); } /** * Validate current user can access the post * * @param Jetpack_Post $post Post object. * * @return WP_Error|Jetpack_Post */ private function validate_access( $post ) { $context = $post->context; if ( ! $this->is_post_type_allowed( $post->post_type ) && ! $this->is_a8c_publication( $post->ID ) ) { return new WP_Error( 'unknown_post', 'Unknown post', 404 ); } switch ( $context ) { case 'edit': if ( ! current_user_can( 'edit_post', $post->ID ) ) { return new WP_Error( 'unauthorized', 'User cannot edit post', 403 ); } break; case 'display': $can_view = $this->user_can_view_post( $post ); if ( is_wp_error( $can_view ) ) { return $can_view; } break; default: return new WP_Error( 'invalid_context', 'Invalid API CONTEXT', 400 ); } return $post; } /** * Validate whether the current user can access the specified post type. * * @param string $post_type The post type to check. * @param string $context The context by which the post data is required (display or edit). * * @return bool */ public function current_user_can_access_post_type( $post_type, $context ) { $post_type_object = $this->get_post_type_object( $post_type ); if ( ! $post_type_object ) { return false; } switch ( $context ) { case 'edit': return current_user_can( $post_type_object->cap->edit_posts ); case 'display': return $post_type_object->public || current_user_can( $post_type_object->cap->read_private_posts ); default: return false; } } /** * Retrieves a post type object by name. * * @param string $post_type The post type to check. * * @return WP_Post_Type|null */ protected function get_post_type_object( $post_type ) { return get_post_type_object( $post_type ); } /** * Is the post type allowed? * * Function copied from class.json-api-endpoints.php. * * @param string $post_type Post type. * * @return bool */ public function is_post_type_allowed( $post_type ) { // if the post type is empty, that's fine, WordPress will default to post if ( empty( $post_type ) ) { return true; } // allow special 'any' type if ( 'any' === $post_type ) { return true; } // check for allowed types if ( in_array( $post_type, $this->get_whitelisted_post_types(), true ) ) { return true; } $post_type_object = get_post_type_object( $post_type ); if ( $post_type_object ) { if ( ! empty( $post_type_object->show_in_rest ) ) { return $post_type_object->show_in_rest; } if ( ! empty( $post_type_object->publicly_queryable ) ) { return $post_type_object->publicly_queryable; } } return ! empty( $post_type_object->public ); } /** * Gets the whitelisted post types that JP should allow access to. * * Function copied from class.json-api-endpoints.php. * * @return array Whitelisted post types. */ public function get_whitelisted_post_types() { $allowed_types = array( 'post', 'page', 'revision' ); /** * Filter the post types Jetpack has access to, and can synchronize with WordPress.com. * * @module json-api * * @since 2.2.3 * * @param array $allowed_types Array of whitelisted post types. Default to `array( 'post', 'page', 'revision' )`. */ $allowed_types = apply_filters( 'rest_api_allowed_post_types', $allowed_types ); return array_unique( $allowed_types ); } /** * Can the user view the post? * * Function copied from class.json-api-endpoints.php and modified. * * @param Jetpack_Post $post Post object. * @return bool|WP_Error */ private function user_can_view_post( $post ) { if ( ! $post || is_wp_error( $post ) ) { return false; } // If the post is of status inherit, check if the parent exists ( different to 0 ) to check for the parent status object. if ( 'inherit' === $post->post_status && 0 !== (int) $post->post_parent ) { $parent_post = get_post( $post->post_parent ); $post_status_obj = get_post_status_object( $parent_post->post_status ); } else { $post_status_obj = get_post_status_object( $post->post_status ); } $authorized = ( $post_status_obj->public || ( is_user_logged_in() && ( ( $post_status_obj->protected && current_user_can( 'edit_post', $post->ID ) ) || ( $post_status_obj->private && current_user_can( 'read_post', $post->ID ) ) || ( 'trash' === $post->post_status && current_user_can( 'edit_post', $post->ID ) ) || 'auto-draft' === $post->post_status ) ) ); if ( ! $authorized ) { return new WP_Error( 'unauthorized', 'User cannot view post', 403 ); } if ( ( new Status() )->is_private_site() && /** * Filter access to a specific post. * * @module json-api * * @since 3.4.0 * * @param bool current_user_can( 'read_post', $post->ID ) Can the current user access the post. * @param WP_Post $post Post data. */ ! apply_filters( 'wpcom_json_api_user_can_view_post', current_user_can( 'read_post', $post->ID ), $post ) ) { return new WP_Error( 'unauthorized', 'User cannot view post', array( 'status_code' => 403, 'error' => 'private_blog', ) ); } if ( strlen( $post->post_password ) && ! current_user_can( 'edit_post', $post->ID ) ) { return new WP_Error( 'unauthorized', 'User cannot view password protected post', array( 'status_code' => 403, 'error' => 'password_protected', ) ); } return true; } /** * Get post ID by name * * Attempts to match name on post title and page path * * @param string $name The post name. * * @return int|WP_Error Post ID on success, WP_Error object on failure */ public function get_post_id_by_name( $name ) { $name = sanitize_title( $name ); if ( ! $name ) { return new WP_Error( 'invalid_post', 'Invalid post', 400 ); } $posts = get_posts( array( 'name' => $name, 'numberposts' => 1, 'post_type' => $this->get_whitelisted_post_types(), ) ); if ( ! $posts || ! isset( $posts[0]->ID ) || ! $posts[0]->ID ) { $page = get_page_by_path( $name ); if ( ! $page ) { return new WP_Error( 'unknown_post', 'Unknown post', 404 ); } return $page->ID; } return (int) $posts[0]->ID; } /** * Get post by name * * Attempts to match name on post title and page path * * @param string $name The post name. * @param string $context (display or edit). * * @return Jetpack_Post|WP_Error Post object on success, WP_Error object on failure **/ public function get_post_by_name( $name, $context ) { $post_id = $this->get_post_id_by_name( $name ); if ( is_wp_error( $post_id ) ) { return $post_id; } return $this->get_post_by_id( $post_id, $context ); } /** * Whether or not the current user is an admin (has option management capabilities). * * @return bool **/ public function user_can_manage() { return current_user_can( 'manage_options' ); } /** * Returns the XMLRPC URL - the site URL including the URL scheme that is used when querying your site's REST API endpoint. * * @return string **/ public function get_xmlrpc_url() { $xmlrpc_scheme = apply_filters( 'wpcom_json_api_xmlrpc_scheme', wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME ) ); return site_url( 'xmlrpc.php', $xmlrpc_scheme ); } /** * Returns a date/time string with the date the site was registered, or a default date/time string otherwise. * * @return string **/ public function get_registered_date() { if ( function_exists( 'get_blog_details' ) ) { $blog_details = get_blog_details(); if ( ! empty( $blog_details->registered ) ) { return WPCOM_JSON_API_Date::format_date( $blog_details->registered ); } } return '0000-00-00T00:00:00+00:00'; } /** * Returns a date/time string with the date the site was last updated, or a default date/time string otherwise. * * @return string **/ public function get_last_update_date() { if ( function_exists( 'get_blog_details' ) ) { $blog_details = get_blog_details(); if ( ! empty( $blog_details->last_updated ) ) { return WPCOM_JSON_API_Date::format_date( $blog_details->last_updated ); } } return '0000-00-00T00:00:00+00:00'; } /** * Returns an array including the current users relevant capabilities. * * @return array **/ public function get_capabilities() { $is_wpcom_blog_owner = wpcom_get_blog_owner() === (int) get_current_user_id(); return array( 'edit_pages' => $this->current_user_can( 'edit_pages' ), 'edit_posts' => $this->current_user_can( 'edit_posts' ), 'edit_others_posts' => $this->current_user_can( 'edit_others_posts' ), 'edit_others_pages' => $this->current_user_can( 'edit_others_pages' ), 'delete_posts' => $this->current_user_can( 'delete_posts' ), 'delete_others_posts' => $this->current_user_can( 'delete_others_posts' ), 'edit_theme_options' => $this->current_user_can( 'edit_theme_options' ), 'edit_users' => $this->current_user_can( 'edit_users' ), 'list_users' => $this->current_user_can( 'list_users' ), 'manage_categories' => $this->current_user_can( 'manage_categories' ), 'manage_options' => $this->current_user_can( 'manage_options' ), 'moderate_comments' => $this->current_user_can( 'moderate_comments' ), 'activate_wordads' => $is_wpcom_blog_owner, 'promote_users' => $this->current_user_can( 'promote_users' ), 'publish_posts' => $this->current_user_can( 'publish_posts' ), 'upload_files' => $this->current_user_can( 'upload_files' ), 'delete_users' => $this->current_user_can( 'delete_users' ), 'remove_users' => $this->current_user_can( 'remove_users' ), 'own_site' => $is_wpcom_blog_owner, /** * Filter whether the Hosting section in Calypso should be available for site. * * @module json-api * * @since 8.2.0 * * @param bool $view_hosting Can site access Hosting section. Default to false. */ 'view_hosting' => apply_filters( 'jetpack_json_api_site_can_view_hosting', false ), 'view_stats' => stats_is_blog_user( $this->blog_id ), 'activate_plugins' => $this->current_user_can( 'activate_plugins' ), 'update_plugins' => $this->current_user_can( 'update_plugins' ), 'export' => $this->current_user_can( 'export' ), 'import' => $this->current_user_can( 'import' ), ); } /** * Whether or not a site is public. * * @return bool **/ public function is_visible() { if ( is_user_logged_in() ) { $current_user = wp_get_current_user(); $visible = (array) get_user_meta( $current_user->ID, 'blog_visibility', true ); $is_visible = true; if ( isset( $visible[ $this->blog_id ] ) ) { $is_visible = (bool) $visible[ $this->blog_id ]; } // null and true are visible return $is_visible; } return null; } /** * Creates and returns an array with logo settings. * * @return array **/ public function get_logo() { // Set an empty response array. $logo_setting = array( 'id' => (int) 0, 'sizes' => array(), 'url' => '', ); // Get current site logo values. $logo_id = get_option( 'site_logo' ); // Update the response array if there's a site logo currenty active. if ( $logo_id ) { $logo_setting['id'] = $logo_id; $logo_setting['url'] = wp_get_attachment_url( $logo_id ); } return $logo_setting; } /** * Returns the timezone string from the site's settings (eg. 'Europe/London'). * * @return string **/ public function get_timezone() { return (string) get_option( 'timezone_string' ); } /** * Returns the GMT offset from the site's settings (eg. 5.5). * * @return float **/ public function get_gmt_offset() { return (float) get_option( 'gmt_offset' ); } /** * Returns the site's login URL. * * @return string **/ public function get_login_url() { return wp_login_url(); } /** * Returns the URL for a site's admin area. * * @return string **/ public function get_admin_url() { return get_admin_url(); } /** * Returns the theme's slug (eg. 'twentytwentytwo') * * @return string **/ public function get_theme_slug() { return get_option( 'stylesheet' ); } /** * Returns a list of errors for broken themes on the site. * * @return array */ public function get_theme_errors() { $themes_with_errors = wp_get_themes( array( 'errors' => true ) ); $theme_errors = array(); foreach ( $themes_with_errors as $theme ) { $errors = $theme->errors(); if ( is_wp_error( $errors ) && ! empty( $errors->get_error_messages() ) ) { $theme_errors[] = array( 'name' => sanitize_title( $theme->get( 'Name' ) ), 'errors' => (array) $errors->get_error_messages(), ); } } return $theme_errors; } /** * Gets the header image data. * * @return bool|object **/ public function get_header_image() { return get_theme_mod( 'header_image_data' ); } /** * Gets the theme background color. * * @return bool|string **/ public function get_background_color() { return get_theme_mod( 'background_color' ); } /** * Get the image default link type. * * @return string **/ public function get_image_default_link_type() { return get_option( 'image_default_link_type' ); } /** * Gets the image thumbnails width. * * @return int **/ public function get_image_thumbnail_width() { return (int) get_option( 'thumbnail_size_w' ); } /** * Gets the image thumbnails height. * * @return int **/ public function get_image_thumbnail_height() { return (int) get_option( 'thumbnail_size_h' ); } /** * Whether cropping is enabled for thumbnails. * * @return string **/ public function get_image_thumbnail_crop() { return get_option( 'thumbnail_crop' ); } /** * Gets the medium sized image setting's width. * * @return int **/ public function get_image_medium_width() { return (int) get_option( 'medium_size_w' ); } /** * Gets the medium sized image setting's height. * * @return int **/ public function get_image_medium_height() { return (int) get_option( 'medium_size_h' ); } /** * Gets the large sized image setting's width. * * @return int **/ public function get_image_large_width() { return (int) get_option( 'large_size_w' ); } /** * Gets the large sized image setting's height. * * @return int **/ public function get_image_large_height() { return (int) get_option( 'large_size_h' ); } /** * Gets the permalink structure as defined in the site's settings. * * @return string **/ public function get_permalink_structure() { return get_option( 'permalink_structure' ); } /** * Gets the default post format * * @return string **/ public function get_default_post_format() { return get_option( 'default_post_format' ); } /** * Gets the default post category * * @return int **/ public function get_default_category() { return (int) get_option( 'default_category' ); } /** * Returns what should be shown on the front page (eg. page or posts) * * @return string **/ public function get_show_on_front() { return get_option( 'show_on_front' ); } /** * Whether or not the front page is set as 'page' to allow a custom front page * * @return bool **/ public function is_custom_front_page() { return ( 'page' === $this->get_show_on_front() ); } /** * Whether or not likes have been enabled on all site posts * * @return bool **/ public function get_default_likes_enabled() { return (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) ); } /** * If sharing has been enabled and there are visible blog services (eg. 'facebook', 'twitter'), returns true. * * @return bool **/ public function get_default_sharing_status() { $default_sharing_status = false; if ( class_exists( 'Sharing_Service' ) ) { $ss = new Sharing_Service(); $blog_services = $ss->get_blog_services(); $default_sharing_status = ! empty( $blog_services['visible'] ); } return (bool) $default_sharing_status; } /** * Displays the current comment status * * @return bool False if closed, true for all other comment statuses. **/ public function get_default_comment_status() { return 'closed' !== get_option( 'default_comment_status' ); } /** * Displays the current site-wide post ping status (for pingbacks and trackbacks) * * @return bool False if closed, true for all other ping statuses. **/ public function default_ping_status() { return 'closed' !== get_option( 'default_ping_status' ); } /** * Whether or not Publicize has been permanently disabled on the site * * @see wpcom/wp-content/admin-plugins/publicize/publicize-wpcom.php * * @return bool Default false. **/ public function is_publicize_permanently_disabled() { $publicize_permanently_disabled = false; if ( function_exists( 'is_publicize_permanently_disabled' ) ) { $publicize_permanently_disabled = is_publicize_permanently_disabled( $this->blog_id ); } return $publicize_permanently_disabled; } /** * Returns the post ID of the static front page. * * @return int **/ public function get_page_on_front() { return (int) get_option( 'page_on_front' ); } /** * Returns the post ID of the page designated as the posts page. * * @return int **/ public function get_page_for_posts() { return (int) get_option( 'page_for_posts' ); } /** * Whether or not headstart is enabled for the site * * @return bool **/ public function is_headstart() { return get_option( 'headstart' ); } /** * The WordPress version on the site. * * @return string **/ public function get_wordpress_version() { global $wp_version; return $wp_version; } /** * Whether or not this is a domain-only site (only relevant on WordPress.com simple sites - false otherwise) * * @return bool **/ public function is_domain_only() { $options = get_option( 'options' ); return ! empty( $options['is_domain_only'] ) ? (bool) $options['is_domain_only'] : false; } /** * Whether or not the blog is set to public (not hidden from search engines) * * @return int 1 for true, 0 for false. **/ public function get_blog_public() { return (int) get_option( 'blog_public' ); } /** * Whether or not the site is in a 'pending automated transfer' state. * * @return bool **/ public function has_pending_automated_transfer() { /** * Filter if a site is in pending automated transfer state. * * @module json-api * * @since 6.4.0 * * @param bool has_site_pending_automated_transfer( $this->blog_id ) * @param int $blog_id Blog identifier. */ return apply_filters( 'jetpack_site_pending_automated_transfer', false, $this->blog_id ); } /** * Whether or not the site has a 'designType' option set as 'store' * * @return bool **/ public function signup_is_store() { return $this->get_design_type() === 'store'; } /** * Return a new WP_Roles instance, which implements a user roles API * * @return WP_Roles **/ public function get_roles() { return new WP_Roles(); } /** * Returns the 'designType' option if set (the site design type), null otherwise. * * @return string|null **/ public function get_design_type() { $options = get_option( 'options' ); return empty( $options['designType'] ) ? null : $options['designType']; } /** * Returns the 'site_goals' option if set (eg. share, promote, educate, sell, showcase). * * @return array **/ public function get_site_goals() { $site_goals_option = get_option( 'site_goals' ); if ( is_array( $site_goals_option ) ) { return $site_goals_option; } return array(); } /** * Return site's launch status. Expanded in class.json-api-site-jetpack.php. * * @return bool False in this case. */ public function get_launch_status() { return false; } /** * Whether a site has any migration meta details - only applicable on WordPress.com * * @see /wpcom/public.api/rest/sal/class.json-api-site-jetpack-shadow.php * * @return null */ public function get_migration_meta() { return null; } /** * Whether a site has a site segment - only applicable on WordPress.com * * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php * * @return false */ public function get_site_segment() { return false; } /** * Whether a site has Vertical ID (used for Starter Templates) - default to only applicable on WordPress.com * * @see /wpcom/public.api/rest/sal/class.json-api-site-wpcom.php * * @return false */ public function get_site_vertical_id() { return false; } /** * Whether a site has a 'site_creation_flow' option set (eg gutenboarding, mobile) - only applicable on WordPress.com * * @see /wpcom-json-endpoints/class.wpcom-json-api-new-site-endpoint.php for more on the option. * * @return bool */ public function get_site_creation_flow() { return get_option( 'site_creation_flow' ); } /** * Whether a site has a 'site_source_slug' option set - only applicable on WordPress.com * * @see /wpcom-json-endpoints/class.wpcom-json-api-new-site-endpoint.php for more on the option. * * @return bool */ public function get_site_source_slug() { return get_option( 'site_source_slug' ); } /** * Return any selected features (used to help recommend plans) * * @return string */ public function get_selected_features() { return get_option( 'selected_features' ); } /** * Return true if the site design was created with a Blank Canvas (empty homepage template), false otherwise. * * @return bool */ public function was_created_with_blank_canvas_design() { return (bool) get_option( 'was_created_with_blank_canvas_design' ); } /** * Get the option storing the Anchor podcast ID that identifies a site as a podcasting site. * * @return string */ public function get_anchor_podcast() { return get_option( 'anchor_podcast' ); } /** * Check if the site is currently being built by the DIFM Lite team. * * @return bool */ public function is_difm_lite_in_progress() { if ( function_exists( 'has_blog_sticker' ) ) { return has_blog_sticker( 'difm-lite-in-progress' ); } elseif ( function_exists( 'wpcomsh_is_site_sticker_active' ) ) { // For atomic sites return wpcomsh_is_site_sticker_active( 'difm-lite-in-progress' ); } return false; } /** * Get the option of site intent which value is coming from the Hero Flow * * @return string */ public function get_site_intent() { return get_option( 'site_intent', '' ); } /** * Get the option of site partner bundle which value is coming from the Partner Flow * * @return string */ public function get_site_partner_bundle() { return get_option( 'site_partner_bundle', '' ); } /** * Get site option to determine if and how to display launchpad onboarding * * @return string */ public function get_launchpad_screen() { return get_option( 'launchpad_screen' ); } /** * Get the option onboarding_segment coming from the Guided Flow * * @return string */ public function get_onboarding_segment() { return get_option( 'onboarding_segment', '' ); } /** * Get site option for completed launchpad checklist tasks * * @return string */ public function get_launchpad_checklist_tasks_statuses() { $launchpad_checklist_tasks_statuses_option = get_option( 'launchpad_checklist_tasks_statuses' ); if ( is_array( $launchpad_checklist_tasks_statuses_option ) ) { return $launchpad_checklist_tasks_statuses_option; } return array(); } /** * Detect whether a site is WordPress.com Staging Site. * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function is_wpcom_staging_site(); /** * Get site option for the production blog id (if is a WP.com Staging Site). * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_wpcom_production_blog_id(); /** * Get site option for the staging blog ids (if it has them) * * @see class.json-api-site-jetpack.php for implementation. */ abstract public function get_wpcom_staging_blog_ids(); /** * Get the site's Blaze eligibility status. * * @return bool */ public function can_blaze() { return (bool) Blaze::site_supports_blaze( $this->blog_id ); } /** * Return site's setup identifier. * * @return string */ public function get_wpcom_site_setup() { return get_option( 'wpcom_site_setup' ); } /** * Returns whether the site is commercial. * * @return mixed * * - `true`: the site is commercial * - `false`: the site is not commercial * - `null`: the commercial status is not yet determined */ public function is_commercial() { // Override if blog has the commercial stickers. if ( function_exists( 'has_blog_sticker' ) ) { $has_not_commercial_sticker = has_blog_sticker( 'jetpack-site-is-not-commercial-override', $this->blog_id ); if ( $has_not_commercial_sticker ) { return false; } $has_commercial_sticker = has_blog_sticker( 'jetpack-site-is-commercial-override', $this->blog_id ); if ( $has_commercial_sticker ) { return true; } } $is_commercial = get_option( '_jetpack_site_is_commercial', null ); return $is_commercial === null ? null : (bool) $is_commercial; } /** * Returns an array of reasons why the site is considered commercial. * * @return array|null */ public function get_is_commercial_reasons() { $reasons = get_option( '_jetpack_site_is_commercial_reason', array() ); // Add override as reason if blog has the commercial stickers. if ( empty( $reasons ) && $this->is_commercial() ) { return array( 'manual-override' ); } elseif ( ! is_array( $reasons ) ) { return array(); } return $reasons; } /** * Returns the site's interface selection e.g. calypso vs. wp-admin * * @return string **/ public function get_wpcom_admin_interface() { return (string) get_option( 'wpcom_admin_interface' ); } /** * Returns whether the site is part of the classic view early release. * * @return bool **/ public function get_wpcom_classic_early_release() { return ! empty( get_option( 'wpcom_classic_early_release' ) ); } /** * Get Zendesk site meta. * * @return array|null */ abstract public function get_zendesk_site_meta(); /** * Detect whether there's a pending plan for this site. * * @return bool */ abstract public function is_pending_plan(); }
| ver. 1.4 |
Github
|
.
| PHP 7.4.3-4ubuntu2.24 | Генерация страницы: 0.01 |
proxy
|
phpinfo
|
Настройка