diff options
author | Anthony G. Basile <blueness@gentoo.org> | 2016-07-28 23:29:30 -0400 |
---|---|---|
committer | Anthony G. Basile <blueness@gentoo.org> | 2016-07-28 23:29:30 -0400 |
commit | 5c4552fad98db23b2698e8a598bf20f42cb430ef (patch) | |
tree | 61fc1f692646288704376f32d914eeef152e375e /plugins/jetpack/sal | |
parent | Update plugin jecpack to 4.0.4 (diff) | |
download | blogs-gentoo-5c4552fad98db23b2698e8a598bf20f42cb430ef.tar.gz blogs-gentoo-5c4552fad98db23b2698e8a598bf20f42cb430ef.tar.bz2 blogs-gentoo-5c4552fad98db23b2698e8a598bf20f42cb430ef.zip |
Update plugin jetpack to 4.1.1
Diffstat (limited to 'plugins/jetpack/sal')
-rw-r--r-- | plugins/jetpack/sal/class.json-api-date.php | 55 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-links.php | 269 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-metadata.php | 39 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-platform-jetpack.php | 12 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-platform.php | 18 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-post-base.php | 667 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-post-jetpack.php | 34 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-site-base.php | 419 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-site-jetpack-base.php | 6 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-site-jetpack.php | 9 | ||||
-rw-r--r-- | plugins/jetpack/sal/class.json-api-token.php | 60 |
11 files changed, 1532 insertions, 56 deletions
diff --git a/plugins/jetpack/sal/class.json-api-date.php b/plugins/jetpack/sal/class.json-api-date.php new file mode 100644 index 00000000..d51247c0 --- /dev/null +++ b/plugins/jetpack/sal/class.json-api-date.php @@ -0,0 +1,55 @@ +<?php + +class WPCOM_JSON_API_Date { + /** + * Returns ISO 8601 formatted datetime: 2011-12-08T01:15:36-08:00 + * + * @param $date_gmt (string) GMT datetime string. + * @param $date (string) Optional. Used to calculate the offset from GMT. + * + * @return string + */ + static function format_date( $date_gmt, $date = null ) { + $timestamp_gmt = strtotime( "$date_gmt+0000" ); + + if ( null === $date ) { + $timestamp = $timestamp_gmt; + $hours = $minutes = $west = 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 ); + } +}
\ No newline at end of file diff --git a/plugins/jetpack/sal/class.json-api-links.php b/plugins/jetpack/sal/class.json-api-links.php new file mode 100644 index 00000000..b5278569 --- /dev/null +++ b/plugins/jetpack/sal/class.json-api-links.php @@ -0,0 +1,269 @@ +<?php + +require_once dirname( __FILE__ ) . '/../class.json-api.php'; + +class WPCOM_JSON_API_Links { + private $api; + private static $instance; + + public static function getInstance() { + if (null === static::$instance) { + static::$instance = new static(); + } + + return static::$instance; + } + + // protect these methods for singleton + protected function __construct() { + $this->api = WPCOM_JSON_API::init(); + } + private function __clone() { } + private function __wakeup() { } + + /** + * 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 + **/ + function get_link() { + $args = func_get_args(); + $format = array_shift( $args ); + $base = WPCOM_JSON_API__BASE; + + $path = array_pop( $args ); + + if ( $path ) { + $path = '/' . ltrim( $path, '/' ); + } + + $args[] = $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%s", $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 ) ); + } + + function get_me_link( $path = '' ) { + return $this->get_link( '/me', $path ); + } + + 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 ); + } + } + + function get_media_link( $blog_id, $media_id, $path = '' ) { + return $this->get_link( '/sites/%d/media/%d', $blog_id, $media_id, $path ); + } + + function get_site_link( $blog_id, $path = '' ) { + return $this->get_link( '/sites/%d', $blog_id, $path ); + } + + function get_post_link( $blog_id, $post_id, $path = '' ) { + return $this->get_link( '/sites/%d/posts/%d', $blog_id, $post_id, $path ); + } + + function get_comment_link( $blog_id, $comment_id, $path = '' ) { + return $this->get_link( '/sites/%d/comments/%d', $blog_id, $comment_id, $path ); + } + + 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 ); + } + + 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 ); + } + + function get_keyring_connection_link( $keyring_token_id, $path = '' ) { + return $this->get_link( '.1/me/keyring-connections/%d', $keyring_token_id, $path ); + } + + 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 $template_path The generic endpoint path, e.g. /sites/%s + * @param $path string The current endpoint path, relative to the version, e.g. /sites/12345 + * @param $method string Request method used to access the endpoint path + * @return string The current version, or otherwise the maximum version available + */ + function get_closest_version_of_endpoint( $template_path, $path, $request_method = 'GET' ) { + static $closest_endpoint_cache; + + if ( ! $closest_endpoint_cache ) { + $closest_endpoint_cache = array(); + } + + 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' ) ) { + return $closest_endpoint_cache[ $template_path ][ $request_method ] = $this->api->version; + } + + static $matches; + if ( empty( $matches ) ) { + $matches = array(); + } else { + // try to match out of saved matches + foreach( $matches as $match ) { + $regex = $match->regex; + if ( preg_match( "#^$regex\$#", $path ) ) { + return $closest_endpoint_cache[ $template_path ][ $request_method ] = $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 + 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'] ) ) { + 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 ( version_compare( $this->api->version, $endpoint['min_version'], '>=') && + version_compare( $this->api->version, $endpoint['max_version'], '<=') ) { + array_push( $matches, (object) array( 'version' => $this->api->version, 'regex' => $endpoint_path_regex ) ); + return $closest_endpoint_cache[ $template_path ][ $request_method ] = $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, (object) $max_version_found ); + 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 + * + * The result is cached for 30 minutes. + * + * @return array Array of endpoint paths, min_versions and max_versions, keyed by last segment of path + **/ + protected function get_endpoint_path_versions() { + + static $cache_result; + + if ( ! empty ( $cache_result ) ) { + return $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 ) { + + // The key contains a serialized path, min_version and max_version + list( $path, $min_version, $max_version ) = unserialize( $key ); + + // 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 ) + ); + } + + $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 ); + } +} diff --git a/plugins/jetpack/sal/class.json-api-metadata.php b/plugins/jetpack/sal/class.json-api-metadata.php new file mode 100644 index 00000000..b4801d76 --- /dev/null +++ b/plugins/jetpack/sal/class.json-api-metadata.php @@ -0,0 +1,39 @@ +<?php + +/** + * Utility classes that don't necessarily have a home yet + */ + +class WPCOM_JSON_API_Metadata { + 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 ) ) ) + return true; + + if ( 0 === strpos( $key, 'geo_' ) ) + return true; + + if ( 0 === strpos( $key, '_wpas_' ) ) + return true; + + return false; + } + + public static function is_internal_only( $key ) { + + if ( 0 === strpos( $key, '_jetpack_') ) + return true; + + if ( 0 === strpos( $key, '_elasticsearch_') ) + return true; + + return false; + } +}
\ No newline at end of file diff --git a/plugins/jetpack/sal/class.json-api-platform-jetpack.php b/plugins/jetpack/sal/class.json-api-platform-jetpack.php index c059b552..6643154c 100644 --- a/plugins/jetpack/sal/class.json-api-platform-jetpack.php +++ b/plugins/jetpack/sal/class.json-api-platform-jetpack.php @@ -1,12 +1,14 @@ <?php -class WPORG_Platform { - static function get_site( $blog_id ) { +require_once dirname( __FILE__ ) . '/class.json-api-platform.php'; + +class WPORG_Platform extends SAL_Platform { + public function get_site( $blog_id ) { require_once dirname( __FILE__ ) . '/class.json-api-site-jetpack.php'; - return new Jetpack_Site( $blog_id ); + return new Jetpack_Site( $blog_id, $this ); } } -function wpcom_get_sal_site( $blog_id ) { - return WPORG_Platform::get_site( $blog_id ); +function wpcom_get_sal_platform( $token ) { + return new WPORG_Platform( $token ); } diff --git a/plugins/jetpack/sal/class.json-api-platform.php b/plugins/jetpack/sal/class.json-api-platform.php index 61c7b557..42ba5b60 100644 --- a/plugins/jetpack/sal/class.json-api-platform.php +++ b/plugins/jetpack/sal/class.json-api-platform.php @@ -1,5 +1,23 @@ <?php +require_once dirname( __FILE__ ) . '/class.json-api-token.php'; + +abstract class SAL_Platform { + public $token; + + function __construct( $token ) { + if ( is_array( $token ) ) { + $token = SAL_Token::from_rest_token( $token ); + } else { + $token = SAL_Token::for_anonymous_user(); + } + + $this->token = $token; + } + + abstract public function get_site( $blog_id ); +} + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { require_once dirname( __FILE__ ) . '/class.json-api-platform-wpcom.php'; } else { diff --git a/plugins/jetpack/sal/class.json-api-post-base.php b/plugins/jetpack/sal/class.json-api-post-base.php new file mode 100644 index 00000000..8a422e19 --- /dev/null +++ b/plugins/jetpack/sal/class.json-api-post-base.php @@ -0,0 +1,667 @@ +<?php +/** + * 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). + **/ + +require_once dirname( __FILE__ ) . '/class.json-api-metadata.php'; +require_once dirname( __FILE__ ) . '/class.json-api-date.php'; +require_once ( ABSPATH . "wp-includes/post.php" ); + +abstract class SAL_Post { + public $post; + public $context; + public $site; + + function __construct( $site, $post, $context ) { + $this->post = $post; + $this->context = $context; + $this->site = $site; + } + + public function __set( $key, $value ) { + $this->post->{ $key } = $value; + } + + public function __get( $key ) { + if ( $key === 'links' ) { + require_once dirname( __FILE__ ) . '/class.json-api-links.php'; + return WPCOM_JSON_API_Links::getInstance(); + } + return $this->post->{ $key }; + } + + public function __call( $name, $arguments ) { + if ( is_callable( array( $this->post, $name ) ) ) { + return call_user_func_array( array( $this->post, $name ), $arguments ); + } else { + trigger_error("Call to undefined method '{$name}'"); + } + } + + public function __isset ( $name ) { + return isset( $this->post->{ $name } ); + } + + abstract public function get_like_count(); + abstract public function is_liked(); + abstract public function is_reblogged(); + abstract public function is_following(); + abstract public function get_global_id(); + abstract public function get_geo(); + + public function get_menu_order() { + return (int) $this->post->menu_order; + } + + public function get_guid() { + return (string) $this->post->guid; + } + + public function get_type() { + return (string) $this->post->post_type; + } + + 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; + } + + 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; + } + + 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; + } + + 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 ); + } + + 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 ( $show ) { + $metadata[] = array( + 'id' => $meta['meta_id'], + 'key' => $meta['meta_key'], + 'value' => maybe_unserialize( $meta['meta_value'] ), + ); + } + } + + if ( ! empty( $metadata ) ) { + return $metadata; + } else { + return false; + } + } + + 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/' ), + ), + ); + + // 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; + } + + public function get_current_user_capabilities() { + return array( + 'publish_post' => current_user_can( 'publish_post', $this->post ), + 'delete_post' => current_user_can( 'delete_post', $this->post ), + 'edit_post' => current_user_can( 'edit_post', $this->post ) + ); + } + + public function get_revisions() { + if ( 'edit' !== $this->context ) { + return false; + } + + $revisions = array(); + $post_revisions = wp_get_post_revisions( $this->post->ID ); + + foreach ( $post_revisions as $_post ) { + $revisions[] = $_post->ID; + } + + return $revisions; + } + + 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; + } + + protected function get_site_link() { + return $this->links->get_site_link( $this->site->get_id() ); + } + + protected function get_post_link( $path = null ) { + return $this->links->get_post_link( $this->site->get_id(), $this->post->ID, $path ); + } + + 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 ) { + 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; + } + + public function get_page_template() { + return (string) get_post_meta( $this->post->ID, '_wp_page_template', true ); + } + + // note this is overridden in jetpack-shadow + 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 ''; + } + } + + 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; + } + + public function get_format() { + $format = (string) get_post_format( $this->post->ID ); + if ( !$format ) { + $format = 'standard'; + } + + return $format; + } + + 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 ); + } + + public function get_date() { + return (string) WPCOM_JSON_API_Date::format_date( $this->post->post_date_gmt, $this->post->post_date ); + } + + public function get_modified_date() { + return (string) WPCOM_JSON_API_Date::format_date( $this->post->post_modified_gmt, $this->post->post_modified ); + } + + 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 ); + } + } + + 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 ) ); + } + } + + public function get_shortlink() { + return (string) esc_url_raw( wp_get_shortlink( $this->post->ID ) ); + } + + 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; + } + } + + 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; + } + + public function get_status() { + return (string) get_post_status( $this->post->ID ); + } + + public function is_sticky() { + return (bool) is_sticky( $this->post->ID ); + } + + public function get_slug() { + return (string) $this->post->post_name; + } + + public function get_password() { + $password = (string) $this->post->post_password; + if ( 'edit' === $this->context ) { + $password = htmlspecialchars_decode( (string) $password, ENT_QUOTES ); + } + return $password; + } + + 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; + } + } + + function the_password_form() { + return __( 'This post is password protected.', 'jetpack' ); + } + + 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, + ); + } + + 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 = (bool) get_post_meta( $this->post->ID, 'switch_like_status', true ); + $post_likes_enabled = $sitewide_likes_enabled; + if ( $post_likes_switched ) { + $post_likes_enabled = ! $post_likes_enabled; + } + return (bool) $post_likes_enabled; + } + + 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; + } + + // No Blog ID parameter. No Post ID parameter. Depends on globals. + // Expects setup_postdata() to already have been run + function get_the_post_content_for_display() { + global $pages, $page; + + $old_pages = $pages; + $old_page = $page; + + $content = join( "\n\n", $pages ); + $content = preg_replace( '/<!--more(.*?)?-->/', '', $content ); + $pages = array( $content ); + $page = 1; + + ob_start(); + the_content(); + $return = ob_get_clean(); + + $pages = $old_pages; + $page = $old_page; + + return $return; + } + + public function get_author() { + if ( 0 == $this->post->post_author ) + return null; + + $show_email = $this->context === 'edit' && current_user_can( 'edit_post', $this->post ); + + $user = get_user_by( 'id', $this->post->post_author ); + + if ( ! $user || is_wp_error( $user ) ) { + trigger_error( 'Unknown user', E_USER_WARNING ); + + return null; + } + + // TODO factor this out + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + $active_blog = get_active_blog_for_user( $user->ID ); + $site_id = $active_blog->get_id(); + $profile_URL = "http://en.gravatar.com/{$user->user_login}"; + } else { + $profile_URL = 'http://en.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, // (string|bool) + '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 ) + ); + + if ($site_id > -1) { + $author['site_ID'] = (int) $site_id; + } + + return (object) $author; + } + + protected abstract function get_avatar_url( $email, $avatar_size = 96 ); + + /** + * Get extra post permalink suggestions + * @return array array of permalink suggestions: 'permalink_URL', 'suggested_slug' + */ + public function get_permalink_suggestions( $title ) { + $suggestions = array(); + list( $suggestions['permalink_URL'], $suggestions['suggested_slug'] ) = get_sample_permalink( $this->post->ID, $title ); + return $suggestions; + } + + 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 ( -1 == get_option( 'blog_public' ) && ! 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; + } + + // TODO: factor this out into site + 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 = $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' ) ) ) { + $metadata = wp_get_attachment_metadata( $media_item->ID ); + if ( isset( $metadata['height'], $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 string $media_id Attachment 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' ) ) ) { + $metadata = wp_get_attachment_metadata( $media_item->ID ); + $response['length'] = $metadata['length']; + $response['exif'] = $metadata; + } + + if ( in_array( $ext, array( 'ogv', 'mp4', 'mov', 'wmv', 'avi', 'mpg', '3gp', '3g2', 'm4v' ) ) ) { + $metadata = wp_get_attachment_metadata( $media_item->ID ); + if ( isset( $metadata['height'], $metadata['width'] ) ) { + $response['height'] = $metadata['height']; + $response['width'] = $metadata['width']; + } + + if ( isset( $metadata['length'] ) ) { + $response['length'] = $metadata['length']; + } + + // 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; + $response['videopress_processing_done'] = true; + if ( '0000-00-00 00:00:00' == $info->finish_date_gmt ) { + $response['videopress_processing_done'] = 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' ) ) ) { + 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; + } +} diff --git a/plugins/jetpack/sal/class.json-api-post-jetpack.php b/plugins/jetpack/sal/class.json-api-post-jetpack.php new file mode 100644 index 00000000..f6803132 --- /dev/null +++ b/plugins/jetpack/sal/class.json-api-post-jetpack.php @@ -0,0 +1,34 @@ +<?php +class Jetpack_Post extends SAL_Post { + public function get_like_count() { + return 0; + } + + public function is_liked() { + return false; + } + + public function is_reblogged() { + return false; + } + + public function is_following() { + return false; + } + + public function get_global_id() { + return ''; + } + + public function get_geo() { + return false; + } + + 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; + } +} diff --git a/plugins/jetpack/sal/class.json-api-site-base.php b/plugins/jetpack/sal/class.json-api-site-base.php index 8c8d5112..c4f6afc8 100644 --- a/plugins/jetpack/sal/class.json-api-site-base.php +++ b/plugins/jetpack/sal/class.json-api-site-base.php @@ -1,14 +1,51 @@ <?php +/* + * WARNING: This file is distributed verbatim in Jetpack. + * There should be nothing WordPress.com specific in this file. + * + * @hide-in-jetpack + */ + +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 ) { + 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; } abstract public function has_videopress(); @@ -55,17 +92,224 @@ abstract class SAL_Site { 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 ); + + + 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 + * + * @return WP_Error or post + */ + private function validate_access( $post ) { + $context = $post->context; + + if ( ! $this->is_post_type_allowed( $post->post_type ) + && + ( ! function_exists( 'is_post_freshly_pressed' ) || ! is_post_freshly_pressed( $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; + } + + // copied from class.json-api-endpoints.php + private 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; + + return false; + } + + // copied from class.json-api-endpoints.php + /** + * Gets the whitelisted post types that JP should allow access to. + * + * @return array Whitelisted post types. + */ + private 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' ); // remove this attribute in favor of 'capabilities' + 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 $this->format_date( $blog_details->registered ); + return WPCOM_JSON_API_Date::format_date( $blog_details->registered ); } } @@ -134,55 +378,134 @@ abstract class SAL_Site { return $logo_setting; } - /** - * Returns ISO 8601 formatted datetime: 2011-12-08T01:15:36-08:00 - * - * @param $date_gmt (string) GMT datetime string. - * @param $date (string) Optional. Used to calculate the offset from GMT. - * - * @return string - */ - function format_date( $date_gmt, $date = null ) { - $timestamp_gmt = strtotime( "$date_gmt+0000" ); + function get_timezone() { + return (string) get_option( 'timezone_string' ); + } - if ( null === $date ) { - $timestamp = $timestamp_gmt; - $hours = $minutes = $west = 0; - } else { - $date_time = date_create( "$date+0000" ); - if ( $date_time ) { - $timestamp = date_format( $date_time, 'U' ); - } else { - $timestamp = 0; - } + function get_gmt_offset() { + return (float) get_option( 'gmt_offset' ); + } - // "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; - } + function get_login_url() { + return wp_login_url(); + } + + function get_admin_url() { + return get_admin_url(); + } + + function get_unmapped_url() { + return get_site_url( $this->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' ); + } - $west = $offset < 0; - $offset = abs( $offset ); - $hours = (int) floor( $offset / 3600 ); - $offset -= $hours * 3600; - $minutes = (int) floor( $offset / 60 ); + 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' ); + } - return (string) gmdate( 'Y-m-d\\TH:i:s', $timestamp ) . sprintf( '%s%02d:%02d', $west ? '-' : '+', $hours, $minutes ); + function get_wordpress_version() { + global $wp_version; + return $wp_version; } } diff --git a/plugins/jetpack/sal/class.json-api-site-jetpack-base.php b/plugins/jetpack/sal/class.json-api-site-jetpack-base.php index fac33f7a..fdeec82c 100644 --- a/plugins/jetpack/sal/class.json-api-site-jetpack-base.php +++ b/plugins/jetpack/sal/class.json-api-site-jetpack-base.php @@ -111,9 +111,9 @@ abstract class Abstract_Jetpack_Site extends SAL_Site { **/ private function is_main_site( $response ) { - if ( isset( $response['options']['main_network_site'], $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 ( isset( $response['options']->main_network_site, $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; } diff --git a/plugins/jetpack/sal/class.json-api-site-jetpack.php b/plugins/jetpack/sal/class.json-api-site-jetpack.php index fcd420db..32dd7a32 100644 --- a/plugins/jetpack/sal/class.json-api-site-jetpack.php +++ b/plugins/jetpack/sal/class.json-api-site-jetpack.php @@ -1,6 +1,7 @@ <?php require_once dirname( __FILE__ ) . '/class.json-api-site-jetpack-base.php'; +require_once dirname( __FILE__ ) . '/class.json-api-post-jetpack.php'; // this code runs on Jetpack (.org) sites class Jetpack_Site extends Abstract_Jetpack_Site { @@ -123,4 +124,12 @@ class Jetpack_Site extends Abstract_Jetpack_Site { function get_ak_vp_bundle_enabled() {} + /** + * Post functions + */ + + function wrap_post( $post, $context ) { + return new Jetpack_Post( $this, $post, $context ); + } + } diff --git a/plugins/jetpack/sal/class.json-api-token.php b/plugins/jetpack/sal/class.json-api-token.php new file mode 100644 index 00000000..10b16f6f --- /dev/null +++ b/plugins/jetpack/sal/class.json-api-token.php @@ -0,0 +1,60 @@ +<?php + +/** + * So that we have a real class instead of just passing around an array + */ +class SAL_Token { + + public $blog_id; + public $user_id; + public $scope; + public $client_id; + public $external_user_id; + public $external_user_code; + public $auth_type; + + 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; + } + + public function is_global() { + return $scope === 'global'; + } + + 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 + ); + } + + static function from_rest_token( $token ) { + $user_id = isset( $token['user_id'] ) ? $token['user_id'] : get_current_user_id(); + $scope = isset( $token['scope'] ) ? $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; + + return new SAL_Token( + $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 + ); + } +} |