diff options
Diffstat (limited to 'plugins/jetpack/sal/class.json-api-site-base.php')
-rw-r--r-- | plugins/jetpack/sal/class.json-api-site-base.php | 647 |
1 files changed, 647 insertions, 0 deletions
diff --git a/plugins/jetpack/sal/class.json-api-site-base.php b/plugins/jetpack/sal/class.json-api-site-base.php new file mode 100644 index 00000000..848ee90c --- /dev/null +++ b/plugins/jetpack/sal/class.json-api-site-base.php @@ -0,0 +1,647 @@ +<?php + +require_once dirname( __FILE__ ) . '/class.json-api-date.php'; +require_once dirname( __FILE__ ) . '/class.json-api-post-base.php'; + +/** + * 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 + **/ +abstract class SAL_Site { + public $blog_id; + public $platform; + + public function __construct( $blog_id, $platform ) { + $this->blog_id = $blog_id; + $this->platform = $platform; + } + + public function get_id() { + return $this->blog_id; + } + + public function get_name() { + return (string) htmlspecialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); + } + + public function get_description() { + return (string) htmlspecialchars_decode( get_bloginfo( 'description' ), ENT_QUOTES ); + } + + public function get_url() { + return (string) home_url(); + } + + public function get_post_count() { + return (int) wp_count_posts( 'post' )->publish; + } + + public function get_quota() { + return null; + } + + abstract public function has_videopress(); + + abstract public function upgraded_filetypes_enabled(); + + abstract public function is_mapped_domain(); + + abstract public function is_redirect(); + + abstract public function is_headstart_fresh(); + + abstract public function featured_images_enabled(); + + abstract public function has_wordads(); + + abstract public function get_frame_nonce(); + + abstract public function get_jetpack_frame_nonce(); + + abstract public function allowed_file_types(); + + abstract public function get_post_formats(); + + abstract public function is_private(); + + abstract public function is_following(); + + abstract public function get_subscribers_count(); + + abstract public function get_locale(); + + abstract public function is_jetpack(); + + abstract public function get_jetpack_modules(); + + abstract public function is_module_active( $module ); + + abstract public function is_vip(); + + abstract public function is_multisite(); + + abstract public function is_single_user_site(); + + abstract public function get_plan(); + + abstract public function get_ak_vp_bundle_enabled(); + + abstract public function get_podcasting_archive(); + + abstract public function get_jetpack_seo_front_page_description(); + + abstract public function get_jetpack_seo_title_formats(); + + abstract public function get_verification_services_codes(); + + abstract public function before_render(); + + abstract public function after_render( &$response ); + + // TODO - factor this out? Seems an odd thing to have on a site + abstract public function after_render_options( &$options ); + + // wrap a WP_Post object with SAL methods + abstract public function wrap_post( $post, $context ); + + abstract protected function is_a8c_publication( $post_id ); + + 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 + ); + } + + public function is_wpcom_atomic() { + return false; + } + + public function is_wpcom_store() { + return false; + } + + public function woocommerce_is_active() { + return false; + } + + public function get_post_by_id( $post_id, $context ) { + // Remove the skyword tracking shortcode for posts returned via the API. + remove_shortcode( 'skyword-tracking' ); + add_shortcode( 'skyword-tracking', '__return_empty_string' ); + + $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 + * + * @return WP_Error or 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 ) ) { + 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; + } + + 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; + } + } + + protected function get_post_type_object( $post_type ) { + return get_post_type_object( $post_type ); + } + + // copied from class.json-api-endpoints.php + 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() ) ) { + return true; + } + + if ( $post_type_object = get_post_type_object( $post_type ) ) { + 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 ); + } + + // copied from class.json-api-endpoints.php + /** + * Gets the whitelisted post types that JP should allow access to. + * + * @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 ); + } + + // copied and modified a little from class.json-api-endpoints.php + private function user_can_view_post( $post ) { + if ( !$post || is_wp_error( $post ) ) { + return false; + } + + if ( 'inherit' === $post->post_status ) { + $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 ( + -1 == get_option( 'blog_public' ) && + /** + * 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 + * + * @return int|object 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 + * @param string $context (display or edit) + * + * @return object 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 ); + } + + function user_can_manage() { + current_user_can( 'manage_options' ); + } + + function get_xmlrpc_url() { + $xmlrpc_scheme = apply_filters( 'wpcom_json_api_xmlrpc_scheme', parse_url( get_option( 'home' ), PHP_URL_SCHEME ) ); + return site_url( 'xmlrpc.php', $xmlrpc_scheme ); + } + + 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'; + } + + function get_capabilities() { + return array( + 'edit_pages' => current_user_can( 'edit_pages' ), + 'edit_posts' => current_user_can( 'edit_posts' ), + 'edit_others_posts' => current_user_can( 'edit_others_posts' ), + 'edit_others_pages' => current_user_can( 'edit_others_pages' ), + 'delete_posts' => current_user_can( 'delete_posts' ), + 'delete_others_posts' => current_user_can( 'delete_others_posts' ), + 'edit_theme_options' => current_user_can( 'edit_theme_options' ), + 'edit_users' => current_user_can( 'edit_users' ), + 'list_users' => current_user_can( 'list_users' ), + 'manage_categories' => current_user_can( 'manage_categories' ), + 'manage_options' => current_user_can( 'manage_options' ), + 'moderate_comments' => current_user_can( 'moderate_comments' ), + 'activate_wordads' => wpcom_get_blog_owner() === (int) get_current_user_id(), + 'promote_users' => current_user_can( 'promote_users' ), + 'publish_posts' => current_user_can( 'publish_posts' ), + 'upload_files' => current_user_can( 'upload_files' ), + 'delete_users' => current_user_can( 'delete_users' ), + 'remove_users' => current_user_can( 'remove_users' ), + 'view_stats' => stats_is_blog_user( $this->blog_id ) + ); + } + + 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; + } + + function get_logo() { + + // Set an empty response array. + $logo_setting = array( + 'id' => (int) 0, + 'sizes' => array(), + 'url' => '', + ); + + // Get current site logo values. + $logo = get_option( 'site_logo' ); + + // Update the response array if there's a site logo currenty active. + if ( $logo && 0 != $logo['id'] ) { + $logo_setting['id'] = $logo['id']; + $logo_setting['url'] = $logo['url']; + + foreach ( $logo['sizes'] as $size => $properties ) { + $logo_setting['sizes'][ $size ] = $properties; + } + } + + return $logo_setting; + } + + function get_timezone() { + return (string) get_option( 'timezone_string' ); + } + + function get_gmt_offset() { + return (float) get_option( 'gmt_offset' ); + } + + function get_login_url() { + return wp_login_url(); + } + + function get_admin_url() { + return get_admin_url(); + } + + function get_unmapped_url() { + return get_site_url( get_current_blog_id() ); + } + + function get_theme_slug() { + return get_option( 'stylesheet' ); + } + + function get_header_image() { + return get_theme_mod( 'header_image_data' ); + } + + function get_background_color() { + return get_theme_mod( 'background_color' ); + } + + function get_image_default_link_type() { + return get_option( 'image_default_link_type' ); + } + + function get_image_thumbnail_width() { + return (int) get_option( 'thumbnail_size_w' ); + } + + function get_image_thumbnail_height() { + return (int) get_option( 'thumbnail_size_h' ); + } + + function get_image_thumbnail_crop() { + return get_option( 'thumbnail_crop' ); + } + + function get_image_medium_width() { + return (int) get_option( 'medium_size_w' ); + } + + function get_image_medium_height() { + return (int) get_option( 'medium_size_h' ); + } + + function get_image_large_width() { + return (int) get_option( 'large_size_w' ); + } + + function get_image_large_height() { + return (int) get_option( 'large_size_h' ); + } + + function get_permalink_structure() { + return get_option( 'permalink_structure' ); + } + + function get_default_post_format() { + return get_option( 'default_post_format' ); + } + + function get_default_category() { + return (int) get_option( 'default_category' ); + } + + function get_show_on_front() { + return get_option( 'show_on_front' ); + } + + function is_custom_front_page() { + return ( 'page' === $this->get_show_on_front() ); + } + + function get_default_likes_enabled() { + return (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) ); + } + + 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; + } + + function get_default_comment_status() { + return 'closed' !== get_option( 'default_comment_status' ); + } + + function default_ping_status() { + return 'closed' !== get_option( 'default_ping_status' ); + } + + 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; + } + + function get_page_on_front() { + return (int) get_option( 'page_on_front' ); + } + + function get_page_for_posts() { + return (int) get_option( 'page_for_posts' ); + } + + function is_headstart() { + return get_option( 'headstart' ); + } + + function get_wordpress_version() { + global $wp_version; + return $wp_version; + } + + function is_domain_only() { + $options = get_option( 'options' ); + return ! empty ( $options['is_domain_only'] ) ? (bool) $options['is_domain_only'] : false; + } + + function get_blog_public() { + return (int) get_option( 'blog_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 + ); + } + + function signup_is_store() { + return $this->get_design_type() === 'store'; + } + + function get_roles() { + return new WP_Roles(); + } + + function get_design_type() { + $options = get_option( 'options' ); + return empty( $options[ 'designType'] ) ? null : $options[ 'designType' ]; + } + + function get_site_goals() { + $options = get_option( 'options' ); + return empty( $options[ 'siteGoals'] ) ? null : $options[ 'siteGoals' ]; + } + + function get_launch_status() { + return false; + } + + function get_site_segment() { + return false; + } +} |