summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony G. Basile <blueness@gentoo.org>2016-07-28 23:29:30 -0400
committerAnthony G. Basile <blueness@gentoo.org>2016-07-28 23:29:30 -0400
commit5c4552fad98db23b2698e8a598bf20f42cb430ef (patch)
tree61fc1f692646288704376f32d914eeef152e375e /plugins/jetpack/sal
parentUpdate plugin jecpack to 4.0.4 (diff)
downloadblogs-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.php55
-rw-r--r--plugins/jetpack/sal/class.json-api-links.php269
-rw-r--r--plugins/jetpack/sal/class.json-api-metadata.php39
-rw-r--r--plugins/jetpack/sal/class.json-api-platform-jetpack.php12
-rw-r--r--plugins/jetpack/sal/class.json-api-platform.php18
-rw-r--r--plugins/jetpack/sal/class.json-api-post-base.php667
-rw-r--r--plugins/jetpack/sal/class.json-api-post-jetpack.php34
-rw-r--r--plugins/jetpack/sal/class.json-api-site-base.php419
-rw-r--r--plugins/jetpack/sal/class.json-api-site-jetpack-base.php6
-rw-r--r--plugins/jetpack/sal/class.json-api-site-jetpack.php9
-rw-r--r--plugins/jetpack/sal/class.json-api-token.php60
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
+ );
+ }
+}