summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYury German <blueknight@gentoo.org>2016-02-12 22:22:00 -0500
committerYury German <blueknight@gentoo.org>2016-02-12 22:22:00 -0500
commit657cafe0e955cf88033597f131aa50835140c617 (patch)
treecf21a30d319cb2a238a6cfb8b4eb3b20b1b5dcff /plugins/jetpack/json-endpoints
parentAdding New Mantra version 2.4.1.1 - Bug 574468 (diff)
downloadblogs-gentoo-657cafe0e955cf88033597f131aa50835140c617.tar.gz
blogs-gentoo-657cafe0e955cf88033597f131aa50835140c617.tar.bz2
blogs-gentoo-657cafe0e955cf88033597f131aa50835140c617.zip
Updating plugins easy-table, jetpack, openid, public-post preview, talbe-of-contents-plus, wordress-mobile-pack - Bug 574468
Diffstat (limited to 'plugins/jetpack/json-endpoints')
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-autosave-post-v1-1-endpoint.php68
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php4
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-autosave-v1-1-endpoint.php46
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-comment-endpoint.php1
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-customcss.php34
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-media-v1-1-endpoint.php4
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-counts-v1-1-endpoint.php107
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-endpoint.php1
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-v1-1-endpoint.php1
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-endpoint.php134
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-get-taxonomy-endpoint.php1
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-comments-endpoint.php11
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-embeds-endpoint.php8
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-invites-endpoint.php124
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-media-v1-1-endpoint.php14
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-endpoint.php5
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-v1-1-endpoint.php42
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-roles-endpoint.php64
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-shortcodes-endpoint.php4
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php29
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-menus-v1-1-endpoint.php3
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-post-endpoint.php26
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-post-v1-1-endpoint.php43
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-publicize-endpoint.php23
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php63
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-site-user-endpoint.php107
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-taxonomy-endpoint.php4
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-comment-endpoint.php18
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-customcss.php50
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-invites-endpoint.php141
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-media-v1-1-endpoint.php12
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-endpoint.php132
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-1-endpoint.php116
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-2-endpoint.php116
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-taxonomy-endpoint.php17
-rw-r--r--plugins/jetpack/json-endpoints/class.wpcom-json-api-update-user-endpoint.php101
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-log-endpoint.php16
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-maybe-auto-update-endpoint.php32
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php7
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php2
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php155
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php12
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php1
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php12
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php39
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-update-option-endpoint.php31
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php93
47 files changed, 1858 insertions, 216 deletions
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-autosave-post-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-autosave-post-v1-1-endpoint.php
new file mode 100644
index 00000000..2db3931a
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-autosave-post-v1-1-endpoint.php
@@ -0,0 +1,68 @@
+<?php
+class WPCOM_JSON_API_Autosave_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_Endpoint {
+ function __construct( $args ) {
+ parent::__construct( $args );
+ }
+
+ // /sites/%s/posts/%d/autosave -> $blog_id, $post_id
+ function callback( $path = '', $blog_id = 0, $post_id = 0 ) {
+
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ $args = $this->query_args();
+
+ $input = $this->input( false );
+
+ if ( ! is_array( $input ) || ! $input ) {
+ return new WP_Error( 'invalid_input', 'Invalid request input', 400 );
+ }
+
+ $post = get_post( $post_id );
+
+ if ( ! $post || is_wp_error( $post ) ) {
+ return new WP_Error( 'unknown_post', 'Unknown post', 404 );
+ }
+
+ if ( ! current_user_can( 'edit_post', $post->ID ) ) {
+ return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
+ }
+
+ $post_data = array (
+ 'post_ID' => $post_id,
+ 'post_title' => $input['title'],
+ 'post_content' => $input['content'],
+ 'post_excerpt' => $input['excerpt'],
+ );
+
+ $preview_url = add_query_arg( 'preview', 'true', get_permalink( $post->ID ) );
+
+ if ( ! wp_check_post_lock( $post->ID ) &&
+ get_current_user_id() == $post->post_author &&
+ ( 'auto-draft' == $post->post_status || 'draft' == $post->post_status )
+ ) {
+ // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked
+ $auto_ID = edit_post( wp_slash( $post_data ) );
+ } else {
+ // Non drafts or other users drafts are not overwritten. The autosave is stored in a special post revision for each user.
+ $auto_ID = wp_create_post_autosave( wp_slash( $post_data ) );
+ $nonce = wp_create_nonce( 'post_preview_' . $post->ID );
+ $preview_url = add_query_arg( array( 'preview_id' => $auto_ID, 'preview_nonce' => $nonce ), $preview_url );
+ }
+
+ $updated_post = get_post( $auto_ID );
+
+ if ( $updated_post && $updated_post->ID && $updated_post->post_modified ) {
+ return array(
+ 'ID' => $auto_ID,
+ 'post_ID' => $post->ID,
+ 'modified' => $this->format_date( $updated_post->post_modified ),
+ 'preview_URL' => $preview_url
+ );
+ } else {
+ return new WP_Error( 'autosave_error', __( 'Autosave encountered an unexpected error', 'jetpack' ), 500 );
+ }
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php
index 0d1b01cf..82359f63 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-comment-endpoint.php
@@ -2,7 +2,7 @@
abstract class WPCOM_JSON_API_Comment_Endpoint extends WPCOM_JSON_API_Endpoint {
- var $comment_object_format = array(
+ public $comment_object_format = array(
// explicitly document and cast all output
'ID' => '(int) The comment ID.',
'post' => "(object>post_reference) A reference to the comment's post.",
@@ -28,7 +28,7 @@ abstract class WPCOM_JSON_API_Comment_Endpoint extends WPCOM_JSON_API_Endpoint {
'meta' => '(object) Meta data',
);
- // var $response_format =& $this->comment_object_format;
+ // public $response_format =& $this->comment_object_format;
function __construct( $args ) {
if ( !$this->response_format ) {
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-autosave-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-autosave-v1-1-endpoint.php
new file mode 100644
index 00000000..806d7fa2
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-autosave-v1-1-endpoint.php
@@ -0,0 +1,46 @@
+<?php
+class WPCOM_JSON_API_Get_Autosave_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_Endpoint {
+ function __construct( $args ) {
+ parent::__construct( $args );
+ }
+
+ // /sites/%s/posts/%d/autosave -> $blog_id, $post_id
+ function callback( $path = '', $blog_id = 0, $post_id = 0 ) {
+
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ $post = get_post( $post_id );
+
+ if ( ! $post || is_wp_error( $post ) ) {
+ return new WP_Error( 'unknown_post', 'Unknown post', 404 );
+ }
+
+ if ( ! current_user_can( 'edit_post', $post->ID ) ) {
+ return new WP_Error( 'unauthorized', 'User cannot edit post', 403 );
+ }
+
+ $autosave = wp_get_post_autosave( $post->ID );
+
+ if ( $autosave ) {
+ $preview_url = add_query_arg( 'preview', 'true', get_permalink( $post->ID ) );
+ $nonce = wp_create_nonce( 'post_preview_' . $post->ID );
+ $preview_url = add_query_arg( array( 'preview_id' => $auto_ID, 'preview_nonce' => $nonce ), $preview_url );
+
+ return array(
+ 'ID' => $autosave->ID,
+ 'author_ID' => $autosave->post_author,
+ 'post_ID' => $autosave->post_parent,
+ 'title' => $autosave->post_title,
+ 'content' => $autosave->post_content,
+ 'excerpt' => $autosave->post_excerpt,
+ 'preview_URL' => $preview_url,
+ 'modified' => $this->format_date( $autosave->post_modified )
+ );
+ } else {
+ return new WP_Error( 'not_found', 'No autosaves exist for this post', 404 );
+ }
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-comment-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-comment-endpoint.php
index af167e0a..97a129e3 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-comment-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-comment-endpoint.php
@@ -15,6 +15,7 @@ class WPCOM_JSON_API_Get_Comment_Endpoint extends WPCOM_JSON_API_Comment_Endpoin
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'comments' );
return $return;
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-customcss.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-customcss.php
new file mode 100644
index 00000000..b3063cd8
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-customcss.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Custom Css endpoint
+ *
+ * https://public-api.wordpress.com/rest/v1.1/sites/$site/customcss/
+ */
+
+class WPCOM_JSON_API_Get_CustomCss_Endpoint extends WPCOM_JSON_API_Endpoint {
+ /**
+ * API callback.
+ */
+ function callback( $path = '', $blog_id = 0 ) {
+ // Switch to the given blog.
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ $args = array(
+ 'css' => Jetpack_Custom_CSS::get_css(),
+ 'preprocessor' => Jetpack_Custom_CSS::get_preprocessor_key(),
+ 'add_to_existing' => ! Jetpack_Custom_CSS::skip_stylesheet(),
+ );
+
+ $defaults = array(
+ 'css' => '',
+ 'preprocessor' => '',
+ 'add_to_existing' => true,
+ );
+ return wp_parse_args( $args, $defaults );
+ }
+}
+
+
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-media-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-media-v1-1-endpoint.php
index 11976509..861cf5de 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-media-v1-1-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-media-v1-1-endpoint.php
@@ -7,6 +7,10 @@ class WPCOM_JSON_API_Get_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
return $blog_id;
}
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ $this->load_theme_functions();
+ }
+
//upload_files can probably be used for other endpoints but we want contributors to be able to use media too
if ( ! current_user_can( 'edit_posts', $media_id ) ) {
return new WP_Error( 'unauthorized', 'User cannot view media', 403 );
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-counts-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-counts-v1-1-endpoint.php
new file mode 100644
index 00000000..6770b2cc
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-counts-v1-1-endpoint.php
@@ -0,0 +1,107 @@
+<?php
+
+class WPCOM_JSON_API_GET_Post_Counts_V1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
+
+ private $whitelist = array( 'publish' );
+
+ /**
+ * Build SQL query
+ *
+ * @param {String} type - post type
+ * @param {Number} [author]
+ * @return {String} SQL query
+ */
+ private function buildCountsQuery( $post_type = 'post', $user_id = null ) {
+ global $wpdb;
+
+ $query = "SELECT post_status as status, count(*) as count ";
+ $query .= "FROM {$wpdb->posts} ";
+ $query .= "WHERE post_type = %s ";
+ if ( isset( $user_id ) ) {
+ $query .= "AND post_author = %d ";
+ }
+
+ $query .= "GROUP BY status";
+
+ return $wpdb->prepare( $query, $post_type, $user_id );
+ }
+
+ /**
+ * Retrive counts using wp_cache
+ *
+ * @param {String} $post_type
+ * @param {Number} [$id]
+ */
+ private function retrieveCounts( $post_type, $id = null) {
+ if ( ! isset( $id ) ) {
+ $counts = array();
+ foreach( (array) wp_count_posts( $post_type ) as $status => $count ) {
+ if ( in_array( $status, $this->whitelist ) && $count > 0 ) {
+ $counts[ $status ] = (int) $count;
+ }
+ };
+
+ return $counts;
+ }
+
+ global $wpdb;
+ $key = 'rest-api-' . $id . '-' . _count_posts_cache_key( $post_type );
+ $counts = wp_cache_get( $key, 'counts' );
+
+ if ( false === $counts ) {
+ $results = $wpdb->get_results( $this->buildCountsQuery( $post_type, $id ) );
+ $counts = $this->filterStatusesByWhiteslist( $results );
+ wp_cache_set( $key, $counts, 'counts' );
+ }
+
+ return $counts;
+ }
+
+ private function filterStatusesByWhiteslist( $in ) {
+ $return = array();
+ foreach( $in as $result) {
+ if ( in_array( $result->status, $this->whitelist ) ) {
+ $return[ $result->status ] = (int) $result->count;
+ }
+ };
+ return $return;
+ }
+
+ public function callback( $path = '', $blog_id = 0, $post_type = 'post' ) {
+ if ( ! get_current_user_id() ) {
+ return new WP_Error( 'authorization_required', __( 'An active access token must be used to retrieve post counts.', 'jetpack' ), 403 );
+ }
+
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ), false );
+
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ if ( ! post_type_exists( $post_type ) ) {
+ return new WP_Error( 'unknown_post_type', __( 'Unknown post type requested.', 'jetpack' ), 404 );
+ }
+
+ $args = $this->query_args();
+ $mine_ID = get_current_user_id();
+
+ if ( current_user_can( 'edit_posts' ) ) {
+ array_push( $this->whitelist, 'draft', 'future', 'pending', 'private', 'trash' );
+ }
+
+ $return = array(
+ 'counts' => (array) array(
+ 'all' => (object) $this->retrieveCounts( $post_type ),
+ 'mine' => (object) $this->retrieveCounts( $post_type, $mine_ID ),
+ )
+ );
+
+ // AUTHOR
+ if ( isset( $args['author'] ) ) {
+ $author_ID = $args['author'];
+ $return['counts']['author'] = (object) $this->retrieveCounts( $post_type, $author_ID );
+ }
+
+ return (object) $return;
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-endpoint.php
index 43b05032..556149e2 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-endpoint.php
@@ -22,6 +22,7 @@ class WPCOM_JSON_API_Get_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
return $return;
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-v1-1-endpoint.php
index 5194daa5..e81c8bae 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-v1-1-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-post-v1-1-endpoint.php
@@ -21,6 +21,7 @@ class WPCOM_JSON_API_Get_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_End
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
return $return;
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-endpoint.php
index 934f7e3d..2054e1bf 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-endpoint.php
@@ -17,6 +17,7 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
'is_private' => '(bool) If the site is a private site or not',
'is_following' => '(bool) If the current user is subscribed to this site in the reader',
'options' => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site. Note: Post formats is deprecated, please see /sites/$id/post-formats/',
+ 'updates' => '(array) An array of available updates for plugins, themes, wordpress, and languages.',
'meta' => '(object) Meta data',
);
@@ -39,6 +40,7 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
$response = $this->build_current_site_response();
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'sites' );
return $response;
@@ -70,6 +72,7 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
$blog_id = (int) $this->api->get_blog_id_for_output();
+ /** This filter is documented in class.json-api-endpoints.php */
$is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
$site_url = get_option( 'siteurl' );
@@ -111,7 +114,7 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
if ( $is_user_logged_in ){
$is_visible = true;
if ( isset( $visible[$blog_id] ) ) {
- $is_visible = $visible[$blog_id];
+ $is_visible = (bool) $visible[$blog_id];
}
// null and true are visible
$response[$key] = $is_visible;
@@ -198,8 +201,9 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
} else {
if ( class_exists( 'Jetpack_Options' ) ) {
$videopress = Jetpack_Options::get_option( 'videopress', array() );
- if ( $videopress['blog_id'] > 0 )
+ if ( isset( $videopress['blog_id'] ) && $videopress['blog_id'] > 0 ) {
$has_videopress = true;
+ }
}
}
@@ -265,6 +269,16 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
$upgraded_filetypes_enabled = true;
}
+ $wordads = false;
+ if ( function_exists( 'has_any_blog_stickers' ) ) {
+ $wordads = has_any_blog_stickers( array( 'wordads-approved', 'wordads-approved-misfits' ), $blog_id );
+ }
+
+ $publicize_permanently_disabled = false;
+ if ( function_exists( 'is_publicize_permanently_disabled' ) ) {
+ $publicize_permanently_disabled = is_publicize_permanently_disabled( $blog_id );
+ }
+
$response[$key] = array(
'timezone' => (string) get_option( 'timezone_string' ),
'gmt_offset' => (float) get_option( 'gmt_offset' ),
@@ -287,15 +301,21 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
'image_medium_height' => (int) get_option( 'medium_size_h' ),
'image_large_width' => (int) get_option( 'large_size_w' ),
'image_large_height' => (int) get_option( 'large_size_h' ),
+ 'permalink_structure' => get_option( 'permalink_structure' ),
'post_formats' => $supported_formats,
+ 'default_post_format' => get_option( 'default_post_format' ),
+ 'default_category' => (int) get_option( 'default_category' ),
'allowed_file_types' => $allowed_file_types,
'show_on_front' => get_option( 'show_on_front' ),
+ /** This filter is documented in modules/likes.php */
'default_likes_enabled' => (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) ),
'default_sharing_status' => (bool) $default_sharing_status,
'default_comment_status' => ( 'closed' == get_option( 'default_comment_status' ) ? false : true ),
'default_ping_status' => ( 'closed' == get_option( 'default_ping_status' ) ? false : true ),
'software_version' => $wp_version,
- 'created_at' => ! empty( $registered_date ) ? $this->format_date( $registered_date ) : '0000-00-00T00:00:00+00:00',
+ 'created_at' => ! empty( $registered_date ) ? $this->format_date( $registered_date ) : '0000-00-00T00:00:00+00:00',
+ 'wordads' => $wordads,
+ 'publicize_permanently_disabled' => $publicize_permanently_disabled,
);
if ( 'page' === get_option( 'show_on_front' ) ) {
@@ -306,21 +326,67 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
if ( $is_jetpack ) {
$response['options']['jetpack_version'] = get_option( 'jetpack_version' );
- if( get_option( 'jetpack_main_network_site' ) ) {
- $response['options']['main_network_site'] = (string) rtrim( get_option( 'jetpack_main_network_site' ), '/' );
- }
+ if ( get_option( 'jetpack_main_network_site' ) ) {
+ $response['options']['main_network_site'] = (string) rtrim( get_option( 'jetpack_main_network_site' ), '/' );
+ }
+
+ if ( is_array( Jetpack_Options::get_option( 'active_modules' ) ) ) {
+ $response['options']['active_modules'] = (array) array_values( Jetpack_Options::get_option( 'active_modules' ) );
+ }
+
+ if ( $jetpack_wp_version = get_option( 'jetpack_wp_version' ) ) {
+ $response['options']['software_version'] = (string) $jetpack_wp_version;
+ } else if ( $jetpack_update = get_option( 'jetpack_updates' ) ) {
+ if ( is_array( $jetpack_update ) && isset( $jetpack_update['wp_version'] ) ) {
+ $response['options']['software_version'] = (string) $jetpack_update['wp_version'];
+ } else {
+ $response[ 'options' ][ 'software_version' ] = null;
+ }
+ } else {
+ $response['options']['software_version'] = null;
+ }
+
+ $response['options']['max_upload_size'] = get_option( 'jetpack_max_upload_size', false );
// Sites have to prove that they are not main_network site.
// If the sync happends right then we should be able to see that we are not dealing with a network site
$response['options']['is_multi_network'] = (bool) get_option( 'jetpack_is_main_network', true );
$response['options']['is_multi_site'] = (bool) get_option( 'jetpack_is_multi_site', true );
+ $file_mod_denied_reason = array();
+ $file_mod_denied_reason['automatic_updater_disabled'] = (bool) get_option( 'jetpack_constant_AUTOMATIC_UPDATER_DISABLED' );
+
+ // WP AUTO UPDATE CORE defaults to minor, '1' if true and '0' if set to false.
+ $file_mod_denied_reason['wp_auto_update_core_disabled'] = ! ( (bool) get_option( 'jetpack_constant_WP_AUTO_UPDATE_CORE', 'minor' ) );
+ $file_mod_denied_reason['is_version_controlled'] = (bool) get_option( 'jetpack_is_version_controlled' );
+
+ // By default we assume that site does have system write access if the value is not set yet.
+ $file_mod_denied_reason['has_no_file_system_write_access'] = ! (bool)( get_option( 'jetpack_has_file_system_write_access', true ) );
+
+ $file_mod_denied_reason['disallow_file_mods'] = (bool) get_option( 'jetpack_constant_DISALLOW_FILE_MODS' );
+
+ $file_mod_disabled_reasons = array();
+ foreach( $file_mod_denied_reason as $reason => $set ) {
+ if ( $set ) {
+ $file_mod_disabled_reasons[] = $reason;
+ }
+ }
+ $response['options']['file_mod_disabled'] = empty( $file_mod_disabled_reasons ) ? false : $file_mod_disabled_reasons;
}
if ( ! current_user_can( 'edit_posts' ) )
unset( $response[$key] );
break;
case 'meta' :
+ /**
+ * Filters the URL scheme used when querying your site's REST API endpoint.
+ *
+ * @module json-api
+ *
+ * @since 3.2.0
+ *
+ * @param string parse_url( get_option( 'home' ), PHP_URL_SCHEME ) URL scheme parsed from home URL.
+ */
$xmlrpc_scheme = apply_filters( 'wpcom_json_api_xmlrpc_scheme', parse_url( get_option( 'home' ), PHP_URL_SCHEME ) );
$xmlrpc_url = site_url( 'xmlrpc.php', $xmlrpc_scheme );
$response[$key] = (object) array(
@@ -335,7 +401,26 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint {
break;
}
}
+
if ( $is_jetpack ) {
+
+ // Add the updates only make them visible if the user has manage options permission.
+ $jetpack_update = (array) get_option( 'jetpack_updates' );
+ if ( ! empty( $jetpack_update ) && current_user_can( 'manage_options' ) ) {
+
+ if ( isset( $jetpack_update['wp_version'] ) ) {
+ // In previous version of Jetpack 3.4, 3.5, 3.6 we synced the wp_version into to jetpack_updates
+ unset( $jetpack_update['wp_version'] );
+ }
+
+ if ( isset( $jetpack_update['site_is_version_controlled'] ) ) {
+ // In previous version of Jetpack 3.4, 3.5, 3.6 we synced the site_is_version_controlled into to jetpack_updates
+ unset( $jetpack_update['site_is_version_controlled'] );
+ }
+
+ $response['updates'] = (array) $jetpack_update;
+ }
+
add_filter( 'option_stylesheet', 'fix_theme_location' );
if ( 'https' !== parse_url( $site_url, PHP_URL_SCHEME ) ) {
remove_filter( 'set_url_scheme', array( $this, 'force_http' ), 10, 3 );
@@ -376,7 +461,7 @@ class WPCOM_JSON_API_List_Post_Formats_Endpoint extends WPCOM_JSON_API_Endpoint
}
}
- $response['formats'] = $supported_formats;
+ $response['formats'] = (object) $supported_formats;
return $response;
}
@@ -414,7 +499,14 @@ class WPCOM_JSON_API_List_Page_Templates_Endpoint extends WPCOM_JSON_API_Endpoin
}
class WPCOM_JSON_API_List_Post_Types_Endpoint extends WPCOM_JSON_API_Endpoint {
- static $post_type_keys_to_include = array( 'name', 'label', 'description', 'map_meta_cap' );
+ static $post_type_keys_to_include = array(
+ 'name' => 'name',
+ 'label' => 'label',
+ 'labels' => 'labels',
+ 'description' => 'description',
+ 'map_meta_cap' => 'map_meta_cap',
+ 'cap' => 'capabilities',
+ );
// /sites/%s/post-types -> $blog_id
function callback( $path = '', $blog_id = 0 ) {
@@ -446,11 +538,14 @@ class WPCOM_JSON_API_List_Post_Types_Endpoint extends WPCOM_JSON_API_Endpoint {
$formatted_post_type_object = array();
// Include only the desired keys in the response
- foreach ( self::$post_type_keys_to_include as $key ) {
- $formatted_post_type_object[ $key ] = $post_type_object->{ $key };
+ foreach ( self::$post_type_keys_to_include as $key => $value ) {
+ $formatted_post_type_object[ $value ] = $post_type_object->{ $key };
}
$formatted_post_type_object['api_queryable'] = $is_queryable;
-
+ $formatted_post_type_object['supports'] = get_all_post_type_supports( $post_type );
+ if ( $this->post_type_supports_tags( $post_type ) ) {
+ $formatted_post_type_object['supports']['tags'] = true;
+ }
$formatted_post_type_objects[] = $formatted_post_type_object;
}
@@ -459,4 +554,21 @@ class WPCOM_JSON_API_List_Post_Types_Endpoint extends WPCOM_JSON_API_Endpoint {
'post_types' => $formatted_post_type_objects
);
}
+
+ function post_type_supports_tags( $post_type ) {
+ if ( in_array( 'post_tag', get_object_taxonomies( $post_type ) ) ) {
+ return true;
+ }
+
+ // the featured content module adds post_tag support
+ // to the post types that are registered for it
+ // however it does so in a way that isn't available
+ // to get_object_taxonomies
+ $featured_content = get_theme_support( 'featured-content' );
+ if ( ! $featured_content || empty( $featured_content[0] ) || empty( $featured_content[0]['post_types'] ) ) {
+ return false;
+ }
+
+ return in_array( $post_type, $featured_content[0]['post_types'] );
+ }
}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-taxonomy-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-taxonomy-endpoint.php
index 6843c254..c97089b4 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-taxonomy-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-taxonomy-endpoint.php
@@ -20,6 +20,7 @@ class WPCOM_JSON_API_Get_Taxonomy_Endpoint extends WPCOM_JSON_API_Taxonomy_Endpo
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'taxonomies' );
return $return;
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-comments-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-comments-endpoint.php
index 942d6962..9b80601d 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-comments-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-comments-endpoint.php
@@ -59,8 +59,9 @@ class WPCOM_JSON_API_List_Comments_Walker extends Walker {
// @todo permissions
class WPCOM_JSON_API_List_Comments_Endpoint extends WPCOM_JSON_API_Comment_Endpoint {
- var $response_format = array(
+ public $response_format = array(
'found' => '(int) The total number of comments found that match the request (ignoring limits, offsets, and pagination).',
+ 'site_ID' => '(int) The site ID',
'comments' => '(array:comment) An array of comment objects.',
);
@@ -236,7 +237,10 @@ class WPCOM_JSON_API_List_Comments_Endpoint extends WPCOM_JSON_API_Comment_Endpo
foreach ( array_keys( $this->response_format ) as $key ) {
switch ( $key ) {
case 'found' :
- $return[$key] = (int) $found;
+ $return[ $key ] = (int) $found;
+ break;
+ case 'site_ID' :
+ $return[ $key ] = (int) $blog_id;
break;
case 'comments' :
$return_comments = array();
@@ -248,10 +252,11 @@ class WPCOM_JSON_API_List_Comments_Endpoint extends WPCOM_JSON_API_Comment_Endpo
}
if ( $return_comments ) {
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'comments', count( $return_comments ) );
}
- $return[$key] = $return_comments;
+ $return[ $key ] = $return_comments;
break;
}
}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-embeds-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-embeds-endpoint.php
index 761ca165..e5cfd7f3 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-embeds-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-embeds-endpoint.php
@@ -14,7 +14,11 @@ class WPCOM_JSON_API_List_Embeds_Endpoint extends WPCOM_JSON_API_Endpoint {
// list em
$output = array( 'embeds' => array() );
-
+
+ if ( ! function_exists( '_wp_oembed_get_object' ) ) {
+ require_once( ABSPATH . WPINC . '/class-oembed.php' );
+ }
+
global $wp_embed;
$oembed = _wp_oembed_get_object();
@@ -32,4 +36,4 @@ class WPCOM_JSON_API_List_Embeds_Endpoint extends WPCOM_JSON_API_Endpoint {
return $output;
}
-} \ No newline at end of file
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-invites-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-invites-endpoint.php
new file mode 100644
index 00000000..1df7cfc8
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-invites-endpoint.php
@@ -0,0 +1,124 @@
+<?php
+class WPCOM_JSON_API_List_Invites_Endpoint extends WPCOM_JSON_API_Endpoint {
+ var $blog_id;
+ var $is_wpcom;
+
+ function callback( $path = '', $blog_id = 0 ) {
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ if ( ! is_multisite() ) {
+ return new WP_Error( 'forbidden', 'To query invites, site must be on a multisite installation.', 403 );
+ }
+
+ if ( ! current_user_can( 'promote_users' ) ) {
+ return new WP_Error( 'unauthorized', 'Your token must have permission to promote users on this blog.', 401 );
+ }
+
+ $this->blog_id = $blog_id;
+ $this->args = $this->query_args();
+ $this->is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM;
+ $this->found = $this->get_found();
+
+ return array(
+ 'found' => $this->found,
+ 'invites' => $this->get_invites(),
+ );
+ }
+
+ /**
+ * Returns the total number of invites, ignoring limits and offsets.
+ * @return int
+ */
+ function get_found() {
+ global $wpdb, $wpcom_invite_users;
+
+ $total = 0;
+ if ( $this->is_wpcom ) {
+ $total = $wpcom_invite_users->count_blog_invitiations( $this->blog_id, null, 'pending' == $this->args['status'] );
+ } else {
+ $total = $invites = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT count( option_id ) FROM $wpdb->options WHERE option_name LIKE %s",
+ 'new_user_%'
+ )
+ );
+ }
+
+ return intval( $total );
+ }
+
+ /**
+ * Returns the invitations for a given site.
+ * @return array
+ */
+ function get_invites() {
+ global $wpdb, $wpcom_invite_users;
+
+ $invites = array();
+ if ( $this->is_wpcom ) {
+ $invites = $wpcom_invite_users->get_blog_invitations(
+ $this->blog_id,
+ null,
+ array(
+ 'offset' => intval( $this->args['offset'] ),
+ 'per_page' => intval( $this->args['number'] ),
+ 'pending_only' => ( 'pending' == $this->args['status'] ),
+ )
+ );
+ } else {
+ $invites = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT * FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_id DESC LIMIT %d, %d",
+ 'new_user_%',
+ intval( $this->args['offset'] ),
+ intval( $this->args['number'] )
+ )
+ );
+ }
+
+ return empty( $invites ) ? array() : array_map( array( $this, 'build_invite' ), $invites );
+ }
+
+ /**
+ * Given an invite, returns an array with expected shape.
+ * @param array $invite
+ * @return array
+ */
+ function build_invite( $invite ) {
+ $invite_key = $this->is_wpcom ? $invite->invite_slug : $invite->option_name;
+ $invite = $this->is_wpcom ? (array) $invite : (array) unserialize( $invite->option_value );
+
+ return array(
+ 'invite_key' => $invite_key,
+ 'role' => $this->is_wpcom ? $invite['meta']['role'] : $invite['role'],
+ 'user' => $this->get_user( $invite ),
+ );
+ }
+
+ /**
+ * Given an invite, returns a user object using the get_author() method in class.json-api-endpoints.php.
+ * @param array $invite
+ * @return array|string
+ */
+ function get_user( $invite ) {
+ if ( ! $this->is_wpcom ) {
+ return $this->get_author( $invite['user_id'] );
+ }
+
+ $user = get_user_by( 'login', $invite['meta']['sent_to'] );
+
+ // If a user did not exist, mock a user to pass to get_author()
+ $no_user = false === $user;
+ if( $no_user ) {
+ $user = new stdClass();
+ $user->comment_author = '';
+ $user->comment_author_url = '';
+ $user->comment_author_email = $invite['meta']['sent_to'];
+ }
+
+ return $this->get_author( $user, $no_user );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-media-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-media-v1-1-endpoint.php
index e53f2c4b..a5b2354c 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-media-v1-1-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-media-v1-1-endpoint.php
@@ -2,8 +2,8 @@
class WPCOM_JSON_API_List_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
- var $date_range = array();
- var $page_handle = array();
+ public $date_range = array();
+ public $page_handle = array();
function callback( $path = '', $blog_id = 0 ) {
$blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
@@ -25,6 +25,10 @@ class WPCOM_JSON_API_List_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
return new WP_Error( 'invalid_number', 'The NUMBER parameter must be less than or equal to 100.', 400 );
}
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ $this->load_theme_functions();
+ }
+
if ( isset( $args['before'] ) ) {
$this->date_range['before'] = $args['before'];
}
@@ -32,12 +36,10 @@ class WPCOM_JSON_API_List_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
$this->date_range['after'] = $args['after'];
}
-
-
- $query = array(
+ $query = array(
'post_type' => 'attachment',
'post_status' => 'inherit',
- 'post_parent' => isset( $args['parent_id'] ) ? $args['parent_id'] : null,
+ 'post_parent' => isset( $args['post_ID'] ) ? $args['post_ID'] : null,
'offset' => isset( $args['offset'] ) ? $args['offset'] : null,
'posts_per_page' => $args['number'],
'post_mime_type' => isset( $args['mime_type'] ) ? $args['mime_type'] : null,
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-endpoint.php
index d1400282..ca1c5a10 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-endpoint.php
@@ -1,9 +1,9 @@
<?php
class WPCOM_JSON_API_List_Posts_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
- var $date_range = array();
+ public $date_range = array();
- var $response_format = array(
+ public $response_format = array(
'found' => '(int) The total number of posts found that match the request (ignoring limits, offsets, and pagination).',
'posts' => '(array:post) An array of post objects.',
);
@@ -232,6 +232,7 @@ class WPCOM_JSON_API_List_Posts_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
}
if ( $posts ) {
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts', count( $posts ) );
}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-v1-1-endpoint.php
index 257e6dfa..1f009365 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-v1-1-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-v1-1-endpoint.php
@@ -1,14 +1,15 @@
<?php
class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_Endpoint {
- var $date_range = array();
- var $modified_range = array();
- var $page_handle = array();
- var $performed_query = null;
+ public $date_range = array();
+ public $modified_range = array();
+ public $page_handle = array();
+ public $performed_query = null;
- var $response_format = array(
+ public $response_format = array(
'found' => '(int) The total number of posts found that match the request (ignoring limits, offsets, and pagination).',
'posts' => '(array:post) An array of post objects.',
+ 'meta' => '(object) Meta data',
);
// /sites/%s/posts/ -> $blog_id
@@ -41,8 +42,7 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E
}
// determine statuses
- $status = $args['status'];
- $status = ( $status ) ? explode( ',', $status ) : array( 'publish' );
+ $status = ( ! empty( $args['status'] ) ) ? explode( ',', $args['status'] ) : array( 'publish' );
if ( is_user_logged_in() ) {
$statuses_whitelist = array(
'publish',
@@ -285,21 +285,33 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E
}
if ( $posts ) {
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts', count( $posts ) );
}
$return[$key] = $posts;
break;
- }
- }
- if ( $is_eligible_for_page_handle && $return['posts'] ) {
- $last_post = end( $return['posts'] );
- reset( $return['posts'] );
+ case 'meta' :
+ if ( ! is_array( $args['type'] ) ) {
+ $return[$key] = (object) array(
+ 'links' => (object) array(
+ 'counts' => (string) $this->get_site_link( $blog_id, 'post-counts/' . $args['type'] ),
+ )
+ );
+ }
- if ( ( $return['found'] > count( $return['posts'] ) ) && $last_post ) {
- $return['meta'] = array();
- $return['meta']['next_page'] = $this->build_page_handle( $last_post, $query );
+ if ( $is_eligible_for_page_handle && $return['posts'] ) {
+ $last_post = end( $return['posts'] );
+ reset( $return['posts'] );
+ if ( ( $return['found'] > count( $return['posts'] ) ) && $last_post ) {
+ if ( ! isset( $return[$key] ) ) {
+ $return[$key] = (object) array();
+ }
+ $return[$key]->next_page = $this->build_page_handle( $last_post, $query );
+ }
+ }
+ break;
}
}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-roles-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-roles-endpoint.php
new file mode 100644
index 00000000..82464c3f
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-roles-endpoint.php
@@ -0,0 +1,64 @@
+<?php
+class WPCOM_JSON_API_List_Roles_Endpoint extends WPCOM_JSON_API_Endpoint {
+
+ var $response_format = array(
+ 'roles' => '(array:role) Array of role objects',
+ );
+
+ static function role_sort( $a, $b ) {
+ $core_role_names = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' );
+ $a_is_core_role = in_array( $a->name, $core_role_names );
+ $b_is_core_role = in_array( $b->name, $core_role_names );
+
+ // if $a is a core_role and $b is not, $a always comes first
+ if ( $a_is_core_role && ! $b_is_core_role ) {
+ return -1;
+ }
+
+ // if $b is a core_role and $a is not, $b always comes first
+ if ( $b_is_core_role && ! $a_is_core_role ) {
+ return 1;
+ }
+
+ // otherwise the one with the > number of capabilities comes first
+ $a_cap_count = count( $a->capabilities );
+ $b_cap_count = count( $b->capabilities );
+
+ if ( $a_cap_count === $b_cap_count ) {
+ return 0;
+ }
+
+ return ( $a_cap_count > $b_cap_count ) ? -1 : 1;
+ }
+
+ // /sites/%s/roles/ -> $blog_id
+ function callback( $path = '', $blog_id = 0 ) {
+
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ if ( ! current_user_can( 'list_users' ) ) {
+ return new WP_Error( 'unauthorized', 'User cannot view roles for specified site', 403 );
+ }
+
+ $roles = array();
+
+ global $wp_roles;
+ $wp_roles->reinit();
+ $role_names = $wp_roles->get_names();
+ $role_keys = array_keys( $role_names );
+
+ foreach ( (array) $role_keys as $role_key ) {
+ $role_details = get_role( $role_key );
+ $role_details->display_name = $role_names[$role_key];
+ $roles[] = $role_details;
+ }
+
+ // Sort the array so roles with the most number of capabilities comes first, then the next role, and so on
+ usort( $roles, array( 'self', 'role_sort' ) );
+
+ return array( 'roles' => $roles );
+ }
+} \ No newline at end of file
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-shortcodes-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-shortcodes-endpoint.php
index 9b2fc1aa..28a42350 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-shortcodes-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-shortcodes-endpoint.php
@@ -19,9 +19,9 @@ class WPCOM_JSON_API_List_Shortcodes_Endpoint extends WPCOM_JSON_API_Endpoint {
foreach ( $shortcode_tags as $tag => $class ) {
if ( '__return_false' == $class )
continue;
- $output['shortcodes'][] = $tag;
+ $output['shortcodes'][] = $tag;
}
return $output;
}
-} \ No newline at end of file
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php
index 21d2c777..225bfa0a 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-users-endpoint.php
@@ -2,8 +2,7 @@
class WPCOM_JSON_API_List_Users_Endpoint extends WPCOM_JSON_API_Endpoint {
var $response_format = array(
- 'found' => '(int) The total number of authors found that match the request (i
-gnoring limits and offsets).',
+ 'found' => '(int) The total number of authors found that match the request (ignoring limits and offsets).',
'users' => '(array:author) Array of user objects',
);
@@ -51,8 +50,24 @@ gnoring limits and offsets).',
if ( $authors_only )
$query['who'] = 'authors';
+ if ( ! empty( $args['search'] ) ) {
+ $query['search'] = $args['search'];
+ }
+
+ if ( ! empty( $args['search_columns'] ) ) {
+ // this `user_search_columns` filter is necessary because WP_User_Query does not allow `display_name` as a search column
+ $this->search_columns = array_intersect( $args['search_columns'], array( 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename', 'display_name' ) );
+ add_filter( 'user_search_columns', array( $this, 'api_user_override_search_columns' ), 10, 3 );
+ }
+
+ if ( ! empty( $args['role'] ) ) {
+ $query['role'] = $args['role'];
+ }
+
$user_query = new WP_User_Query( $query );
+ remove_filter( 'user_search_columns', array( $this, 'api_user_override_search_columns' ) );
+
$return = array();
foreach ( array_keys( $this->response_format ) as $key ) {
switch ( $key ) {
@@ -61,9 +76,15 @@ gnoring limits and offsets).',
break;
case 'users' :
$users = array();
+ $is_multisite = is_multisite();
foreach ( $user_query->get_results() as $u ) {
$the_user = $this->get_author( $u, true );
if ( $the_user && ! is_wp_error( $the_user ) ) {
+ $userdata = get_userdata( $u );
+ $the_user->roles = ! is_wp_error( $userdata ) ? $userdata->roles : array();
+ if ( $is_multisite ) {
+ $the_user->is_super_admin = user_can( $the_user->ID, 'manage_network' );
+ }
$users[] = $the_user;
}
}
@@ -75,4 +96,8 @@ gnoring limits and offsets).',
return $return;
}
+
+ function api_user_override_search_columns( $search_columns, $search ) {
+ return $this->search_columns;
+ }
}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-menus-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-menus-v1-1-endpoint.php
index 9d08935a..c77ce8d3 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-menus-v1-1-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-menus-v1-1-endpoint.php
@@ -497,6 +497,7 @@ class WPCOM_JSON_API_Menus_Update_Menu_Endpoint extends WPCOM_JSON_API_Menus_Abs
}
$data = $this->input( true, false );
+ $data['id'] = $menu_id;
$data = $this->complexify( array( $data ) );
if ( is_wp_error( $data ) ) {
return $data;
@@ -652,7 +653,7 @@ class WPCOM_JSON_API_Menus_Get_Menu_Endpoint extends WPCOM_JSON_API_Menus_Abstra
$menu->items = $items;
- return $this->simplify( $menu );
+ return array( 'menu' => $this->simplify( $menu ) );
}
}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-post-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-post-endpoint.php
index 6366354a..744eaf0a 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-post-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-post-endpoint.php
@@ -1,7 +1,7 @@
<?php
abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
- var $post_object_format = array(
+ public $post_object_format = array(
// explicitly document and cast all output
'ID' => '(int) The post ID.',
'site_ID' => '(int) The site ID.',
@@ -53,7 +53,7 @@ abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
'capabilities' => '(object) List of post-specific permissions for the user; publish_post, edit_post, delete_post',
);
- // var $response_format =& $this->post_object_format;
+ // public $response_format =& $this->post_object_format;
function __construct( $args ) {
if ( is_array( $this->post_object_format ) && isset( $this->post_object_format['format'] ) ) {
@@ -72,7 +72,16 @@ abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
// Default whitelisted meta keys.
$whitelisted_meta = array( '_thumbnail_id' );
- // whitelist of metadata that can be accessed
+ /**
+ * Filters the meta keys accessible by the REST API.
+ * @see https://developer.wordpress.com/2013/04/26/custom-post-type-and-metadata-support-in-the-rest-api/
+ *
+ * @module json-api
+ *
+ * @since 2.2.3
+ *
+ * @param array $whitelisted_meta Array of metadata that is accessible by the REST API.
+ */
if ( in_array( $key, apply_filters( 'rest_api_allowed_public_metadata', $whitelisted_meta ) ) )
return true;
@@ -100,6 +109,7 @@ abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
function get_post_by( $field, $field_value, $context = 'display' ) {
global $blog_id;
+ /** This filter is documented in class.json-api-endpoints.php */
$is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
if ( defined( 'GEO_LOCATION__CLASS' ) && class_exists( GEO_LOCATION__CLASS ) ) {
@@ -170,7 +180,15 @@ abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
$response = array();
+ $fields = null;
+ if ( 'display' === $context && ! empty( $this->api->query['fields'] ) ) {
+ $fields = array_fill_keys( array_map( 'trim', explode( ',', $this->api->query['fields'] ) ), true );
+ }
+
foreach ( array_keys( $this->post_object_format ) as $key ) {
+ if ( $fields !== null && ! isset( $fields[$key] ) ) {
+ continue;
+ }
switch ( $key ) {
case 'ID' :
// explicitly cast all output
@@ -271,6 +289,7 @@ abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
$response[$key] = (bool) pings_open( $post->ID );
break;
case '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( $post->ID, 'switch_like_status', true );
$post_likes_enabled = $sitewide_likes_enabled;
@@ -613,6 +632,7 @@ abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint {
$result['duration'] = (int) $metadata['duration'];
}
+ /** This filter is documented in class.jetpack-sync.php */
return (object) apply_filters( 'get_attachment', $result );
}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-post-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-post-v1-1-endpoint.php
index 94574f92..b5f3cf8e 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-post-v1-1-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-post-v1-1-endpoint.php
@@ -1,7 +1,7 @@
<?php
abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint {
- var $post_object_format = array(
+ public $post_object_format = array(
// explicitly document and cast all output
'ID' => '(int) The post ID.',
'site_ID' => '(int) The site ID.',
@@ -50,9 +50,10 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
'metadata' => '(array) Array of post metadata keys and values. All unprotected meta keys are available by default for read requests. Both unprotected and protected meta keys are available for authenticated requests with access. Protected meta keys can be made available with the <code>rest_api_allowed_public_metadata</code> filter.',
'meta' => '(object) API result meta data',
'capabilities' => '(object) List of post-specific permissions for the user; publish_post, edit_post, delete_post',
+ 'other_URLs' => '(object) List of URLs for this post. Permalink and slug suggestions.',
);
- // var $response_format =& $this->post_object_format;
+ // public $response_format =& $this->post_object_format;
function __construct( $args ) {
if ( is_array( $this->post_object_format ) && isset( $this->post_object_format['format'] ) ) {
@@ -72,7 +73,8 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
$whitelisted_meta = array( '_thumbnail_id' );
// whitelist of metadata that can be accessed
- if ( in_array( $key, apply_filters( 'rest_api_allowed_public_metadata', $whitelisted_meta ) ) )
+ /** 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_' ) )
@@ -81,8 +83,8 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
if ( 0 === strpos( $key, '_wpas_' ) )
return true;
- return false;
- }
+ return false;
+ }
function the_password_form() {
return __( 'This post is password protected.', 'jetpack' );
@@ -99,6 +101,7 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
function get_post_by( $field, $field_value, $context = 'display' ) {
global $blog_id;
+ /** This filter is documented in class.json-api-endpoints.php */
$is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
if ( defined( 'GEO_LOCATION__CLASS' ) && class_exists( GEO_LOCATION__CLASS ) ) {
@@ -272,6 +275,7 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
);
break;
case '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( $post->ID, 'switch_like_status', true );
$post_likes_enabled = $sitewide_likes_enabled;
@@ -459,11 +463,27 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
'likes' => (string) $this->get_post_link( $this->api->get_blog_id_for_output(), $post->ID, 'likes/' ),
),
);
+
+ // add autosave link if a more recent autosave exists
+ if ( 'edit' === $context ) {
+ $autosave = wp_get_post_autosave( $post_id );
+ if ( $autosave && $autosave->post_modified > $post->post_modified )
+ $response[$key]->links->autosave = (string) $this->get_post_link( $this->api->get_blog_id_for_output(), $post->ID ) . '/autosave';
+ }
+
break;
case 'capabilities' :
$response[$key] = $capabilities;
break;
+ case 'other_URLs' :
+ $other_urls = array();
+ if ( 'publish' !== $post->post_status ) {
+ $other_urls = $this->get_post_permalink_suggestions( $post->ID, $post->post_title );
+ }
+
+ $response[$key] = (object) $other_urls;
+ break;
}
}
@@ -612,6 +632,7 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
$result['duration'] = (int) $metadata['duration'];
}
+ /** This filter is documented in class.jetpack-sync.php */
return (object) apply_filters( 'get_attachment', $result );
}
@@ -629,6 +650,18 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
}
/**
+ * Get extra post permalink suggestions
+ * @param int $postID
+ * @param string $title
+ * @return array array of permalink suggestions: 'permalink_URL', 'suggested_slug'
+ */
+ function get_post_permalink_suggestions( $postID, $title ) {
+ $suggestions = array();
+ list( $suggestions['permalink_URL'], $suggestions['suggested_slug'] ) = get_sample_permalink( $postID, $title );
+ return $suggestions;
+ }
+
+ /**
* Get post ID by name
*
* Attempts to match name on post title and page path
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-publicize-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-publicize-endpoint.php
index 5220e112..bccfa84d 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-publicize-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-publicize-endpoint.php
@@ -115,11 +115,11 @@ class WPCOM_JSON_API_Get_Connection_Endpoint extends WPCOM_JSON_API_Endpoint {
// Verify that user has permission to view this connection
if ( $current_user->ID != $connection['user_ID'] && 0 != $connection['user_ID'] ) {
- return new WP_Error( 'authorization_required', 'You do not have permission to access this resource.', 403 );
- }
+ return new WP_Error( 'authorization_required', 'You do not have permission to access this resource.', 403 );
+ }
if ( empty( $connection ) ) {
- return new WP_Error( 'unknown_connection', 'Connection not found.', 404 );
+ return new WP_Error( 'unknown_connection', 'Connection not found.', 404 );
}
return $connection;
@@ -148,19 +148,28 @@ class WPCOM_JSON_API_Delete_Connection_Endpoint extends WPCOM_JSON_API_Endpoint
$connection = WPCOM_JSON_API_Get_Connection_Endpoint::get_connection_by_id( $connection_id );
if ( empty( $connection ) ) {
- return new WP_Error( 'unknown_connection', 'Connection not found.', 404 );
+ return new WP_Error( 'unknown_connection', 'Connection not found.', 404 );
}
// Verify that user has permission to view this connection
if ( $current_user->ID != $connection['user_ID'] && 0 != $connection['user_ID'] ) {
- return new WP_Error( 'authorization_required', 'You do not have permission to access this resource.', 403 );
- }
+ return new WP_Error( 'authorization_required', 'You do not have permission to access this resource.', 403 );
+ }
// Remove publicize connections related to the connection
$publicize = new Publicize();
$is_deleted = ( false !== $publicize->disconnect( $connection['service'], $connection_id ) );
if ( $is_deleted ) {
+ /**
+ * Fires when a Publicize connection is deleted.
+ *
+ * @module json-api
+ *
+ * @since 3.2.0
+ *
+ * @param int $connection_id Publicize connection ID.
+ */
do_action( 'rest_api_delete_publicize_connection', $connection_id );
}
@@ -169,4 +178,4 @@ class WPCOM_JSON_API_Delete_Connection_Endpoint extends WPCOM_JSON_API_Endpoint
'deleted' => $is_deleted
);
}
-} \ No newline at end of file
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php
index 1918182b..7588da17 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-endpoint.php
@@ -30,6 +30,15 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
}
if ( 'GET' === $this->api->method ) {
+ /**
+ * Fires on each GET request to a specific endpoint.
+ *
+ * @module json-api
+ *
+ * @since 3.2.0
+ *
+ * @param string sites.
+ */
do_action( 'wpcom_json_api_objects', 'sites' );
return $this->get_settings_response();
} else if ( 'POST' === $this->api->method ) {
@@ -76,6 +85,7 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
$response_format = self::$site_format;
$blog_id = (int) $this->api->get_blog_id_for_output();
+ /** This filter is documented in class.json-api-endpoints.php */
$is_jetpack = true === apply_filters( 'is_jetpack_site', false, $blog_id );
foreach ( array_keys( $response_format ) as $key ) {
@@ -111,6 +121,16 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
)
);
+ $eventbrite_api_token = (int) get_option( 'eventbrite_api_token' );
+ if ( 0 === $eventbrite_api_token ) {
+ $eventbrite_api_token = null;
+ }
+
+ $holiday_snow = false;
+ if ( function_exists( 'jetpack_holiday_snow_option_name' ) ) {
+ $holiday_snow = (bool) get_option( jetpack_holiday_snow_option_name() );
+ }
+
$response[$key] = array(
// also exists as "options"
@@ -125,7 +145,7 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
'jetpack_relatedposts_enabled' => (bool) $jetpack_relatedposts_options[ 'enabled' ],
'jetpack_relatedposts_show_headline' => (bool) $jetpack_relatedposts_options[ 'show_headline' ],
'jetpack_relatedposts_show_thumbnails' => (bool) $jetpack_relatedposts_options[ 'show_thumbnails' ],
- 'default_category' => get_option('default_category'),
+ 'default_category' => (int) get_option('default_category'),
'post_categories' => (array) $post_categories,
'default_post_format' => get_option( 'default_post_format' ),
'default_pingback_flag' => (bool) get_option( 'default_pingback_flag' ),
@@ -156,6 +176,8 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
'jetpack_comment_likes_enabled' => (bool) get_option( 'jetpack_comment_likes_enabled', false ),
'twitter_via' => (string) get_option( 'twitter_via' ),
'jetpack-twitter-cards-site-tag' => (string) get_option( 'jetpack-twitter-cards-site-tag' ),
+ 'eventbrite_api_token' => $eventbrite_api_token,
+ 'holidaysnow' => $holiday_snow
);
if ( class_exists( 'Sharing_Service' ) ) {
@@ -190,7 +212,16 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
// $this->input() retrieves posted arguments whitelisted and casted to the $request_format
// specs that get passed in when this class is instantiated
- $input = $this->input();
+ /**
+ * Filters the settings to be updated on the site.
+ *
+ * @module json-api
+ *
+ * @since 3.6.0
+ *
+ * @param array $input Associative array of site settings to be updated.
+ */
+ $input = apply_filters( 'rest_api_update_site_settings', $this->input() );
$jetpack_relatedposts_options = array();
$sharing_options = array();
@@ -267,7 +298,9 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
}
$enabled_or_disabled = $wga['code'] ? 'enabled' : 'disabled';
- bump_stats_extras( 'google-analytics', $enabled_or_disabled );
+
+ /** This action is documented in modules/widgets/social-media-icons.php */
+ do_action( 'jetpack_bump_stats_extras', 'google-analytics', $enabled_or_disabled );
$business_plugins = WPCOM_Business_Plugins::instance();
$business_plugins->activate_plugin( 'wp-google-analytics' );
@@ -291,6 +324,30 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint {
$sharing_options[ $key ] = $value;
break;
+ // Keyring token option
+ case 'eventbrite_api_token':
+ // These options can only be updated for sites hosted on WordPress.com
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
+ if ( delete_option( $key ) ) {
+ $updated[ $key ] = null;
+ }
+ } else if ( update_option( $key, $value ) ) {
+ $updated[ $key ] = (int) $value;
+ }
+ }
+ break;
+
+ case 'holidaysnow':
+ if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) {
+ if ( function_exists( 'jetpack_holiday_snow_option_name' ) && delete_option( jetpack_holiday_snow_option_name() ) ) {
+ $updated[ $key ] = false;
+ }
+ } else if ( function_exists( 'jetpack_holiday_snow_option_name' ) && update_option( jetpack_holiday_snow_option_name(), 'letitsnow' ) ) {
+ $updated[ $key ] = true;
+ }
+ break;
+
// no worries, we've already whitelisted and casted arguments above
default:
if ( update_option( $key, $value ) ) {
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-user-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-user-endpoint.php
new file mode 100644
index 00000000..84a83849
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-user-endpoint.php
@@ -0,0 +1,107 @@
+<?php
+
+class WPCOM_JSON_API_Site_User_Endpoint extends WPCOM_JSON_API_Endpoint {
+
+ public static $user_format = array(
+ 'ID' => '(int) The ID of the user',
+ 'login' => '(string) The login username of the user',
+ 'email' => '(string) The email of the user',
+ 'name' => '(string) The name to display for the user',
+ 'first_name' => '(string) The first name of the user',
+ 'last_name' => '(string) The last name of the user',
+ 'nice_name' => '(string) The nice_name to display for the user',
+ 'URL' => '(string) The primary blog of the user',
+ 'avatar_URL' => '(url) Gravatar image URL',
+ 'profile_URL' => '(url) Gravatar Profile URL',
+ 'site_ID' => '(int) ID of the user\'s primary blog',
+ 'roles' => '(array) The roles of the user',
+ );
+
+ // /sites/%s/users/%d -> $blog_id, $user_id
+ function callback( $path = '', $blog_id = 0, $user_id = 0 ) {
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+ if ( ! current_user_can_for_blog( $blog_id, 'list_users' ) ) {
+ return new WP_Error( 'unauthorized', 'User cannot view users for specified site', 403 );
+ }
+
+ // Get the user by ID or login
+ $get_by = false !== strpos( $path, '/users/login:' ) ? 'login' : 'id';
+ $user = get_user_by( $get_by, $user_id );
+
+ if ( ! $user ) {
+ return new WP_Error( 'unknown_user', 'Unknown user', 404 );
+ }
+
+ if ( ! is_user_member_of_blog( $user->ID, $blog_id ) ) {
+ return new WP_Error( 'unknown_user_for_site', 'Unknown user for site', 404 );
+ }
+
+ if ( 'GET' === $this->api->method ) {
+ return $this->get_user( $user->ID );
+ } else if ( 'POST' === $this->api->method ) {
+ if ( ! current_user_can_for_blog( $blog_id, 'promote_users' ) ) {
+ return new WP_Error( 'unauthorized', 'User cannot promote users for specified site', 403 );
+ }
+ if ( get_current_user_id() == $user_id ) {
+ return new WP_Error( 'unauthorized', 'You cannot change your own role', 403 );
+ }
+ return $this->update_user( $user_id );
+ } else {
+ return new WP_Error( 'bad_request', 'An unsupported request method was used.' );
+ }
+ }
+
+ public function get_user( $user_id ) {
+ $the_user = $this->get_author( $user_id, true );
+ if ( $the_user && ! is_wp_error( $the_user ) ) {
+ $userdata = get_userdata( $user_id );
+ $the_user->roles = ! is_wp_error( $userdata ) ? $userdata->roles : array();
+ }
+
+ return $the_user;
+ }
+
+ /**
+ * Updates user data
+ *
+ * @return (array)
+ */
+ public function update_user( $user_id ) {
+ $input = $this->input();
+ $user['ID'] = $user_id;
+ if ( ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
+ foreach ( $input as $key => $value ) {
+ if ( ! is_array( $value ) ) {
+ $value = trim( $value );
+ }
+ $value = wp_unslash( $value );
+ switch ( $key ) {
+ case 'first_name':
+ case 'last_name':
+ $user[ $key ] = $value;
+ break;
+ case 'display_name':
+ case 'name':
+ $user[ 'display_name' ] = $value;
+ break;
+ }
+ }
+ }
+ if ( isset( $input[ 'roles' ] ) ) {
+ if ( is_array( $input['roles'] ) ) {
+ $user['role'] = $input['roles'][0];
+ } else {
+ $user['role'] = $input['roles'];
+ }
+ }
+ $result = wp_update_user( $user );
+ if ( is_wp_error( $result ) ) {
+ return $result;
+ }
+ return $this->get_user( $user_id );
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-taxonomy-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-taxonomy-endpoint.php
index 483c0b70..bac3a4f4 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-taxonomy-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-taxonomy-endpoint.php
@@ -1,6 +1,6 @@
<?php
abstract class WPCOM_JSON_API_Taxonomy_Endpoint extends WPCOM_JSON_API_Endpoint {
- var $category_object_format = array(
+ public $category_object_format = array(
'ID' => '(int) The category ID.',
'name' => "(string) The name of the category.",
'slug' => "(string) The slug of the category.",
@@ -10,7 +10,7 @@ abstract class WPCOM_JSON_API_Taxonomy_Endpoint extends WPCOM_JSON_API_Endpoint
'meta' => '(object) Meta data',
);
- var $tag_object_format = array(
+ public $tag_object_format = array(
'ID' => '(int) The tag ID.',
'name' => "(string) The name of the tag.",
'slug' => "(string) The slug of the tag.",
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-comment-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-comment-endpoint.php
index 9c96d422..517e6710 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-comment-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-comment-endpoint.php
@@ -50,7 +50,20 @@ class WPCOM_JSON_API_Update_Comment_Endpoint extends WPCOM_JSON_API_Comment_Endp
return new WP_Error( 'unknown_post', 'Unknown post', 404 );
}
- if ( -1 == get_option( 'blog_public' ) && ! apply_filters( 'wpcom_json_api_user_is_member_of_blog', is_user_member_of_blog() ) && ! is_super_admin() ) {
+ if (
+ -1 == get_option( 'blog_public' ) &&
+ /**
+ * Filter allowing non-registered users on the site to comment.
+ *
+ * @module json-api
+ *
+ * @since 3.4.0
+ *
+ * @param bool is_user_member_of_blog() Is the user member of the site.
+ */
+ ! apply_filters( 'wpcom_json_api_user_is_member_of_blog', is_user_member_of_blog() ) &&
+ ! is_super_admin()
+ ) {
return new WP_Error( 'unauthorized', 'User cannot create comments', 403 );
}
@@ -127,6 +140,7 @@ class WPCOM_JSON_API_Update_Comment_Endpoint extends WPCOM_JSON_API_Comment_Endp
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'comments' );
return $return;
}
@@ -215,6 +229,7 @@ class WPCOM_JSON_API_Update_Comment_Endpoint extends WPCOM_JSON_API_Comment_Endp
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'comments' );
return $return;
}
@@ -236,6 +251,7 @@ class WPCOM_JSON_API_Update_Comment_Endpoint extends WPCOM_JSON_API_Comment_Endp
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'comments' );
wp_delete_comment( $comment->comment_ID );
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-customcss.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-customcss.php
new file mode 100644
index 00000000..954de00b
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-customcss.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Custom Css update endpoint
+ *
+ * https://public-api.wordpress.com/rest/v1.1/sites/$site/customcss/
+ */
+
+class WPCOM_JSON_API_Update_CustomCss_Endpoint extends WPCOM_JSON_API_Endpoint {
+ /**
+ * API callback.
+ */
+ function callback( $path = '', $blog_id = 0 ) {
+ // Switch to the given blog.
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ if ( ! current_user_can( 'edit_theme_options' ) ) {
+ return new WP_Error( 'unauthorized', 'User is not authorized to access custom css', 403 );
+ }
+
+ $args = $this->input();
+ if ( empty( $args ) || ! is_array( $args ) ) {
+ return new WP_Error( 'no_data', 'No data was provided.', 400 );
+ }
+ $save_args = array(
+ 'css' => $args['css'],
+ 'preprocessor' => $args['preprocessor'],
+ 'add_to_existing' => $args['add_to_existing'],
+ );
+ Jetpack_Custom_CSS::save( $save_args );
+
+ $current = array(
+ 'css' => Jetpack_Custom_CSS::get_css(),
+ 'preprocessor' => Jetpack_Custom_CSS::get_preprocessor_key(),
+ 'add_to_existing' => ! Jetpack_Custom_CSS::skip_stylesheet(),
+ );
+
+ $defaults = array(
+ 'css' => '',
+ 'preprocessor' => '',
+ 'add_to_existing' => true,
+ );
+ return wp_parse_args( $current, $defaults );
+ }
+}
+
+
+
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-invites-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-invites-endpoint.php
new file mode 100644
index 00000000..85a1ab92
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-invites-endpoint.php
@@ -0,0 +1,141 @@
+<?php
+class WPCOM_JSON_API_Update_Invites_Endpoint extends WPCOM_JSON_API_Endpoint {
+ public $blog_id;
+ public $invite_id;
+ public $is_wpcom;
+ public $invite;
+
+ function callback( $path = '', $blog_id = 0, $invite_id = 0 ) {
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ if ( ! is_multisite() ) {
+ return new WP_Error( 'forbidden', 'To modify invites, site must be on a multisite installation.', 403 );
+ }
+
+ if ( ! current_user_can( 'promote_users' ) ) {
+ return new WP_Error( 'unauthorized', 'Your token must have permission to promote users on this blog.', 401 );
+ }
+
+ $this->blog_id = $blog_id;
+ $this->invite_id = $invite_id;
+ $this->is_wpcom = defined( 'IS_WPCOM' ) && IS_WPCOM;
+
+ $invite = $this->get_invite();
+ if ( false === $invite ) {
+ return new WP_Error( 'unknown_invite', 'Requested invite was not found.', 404 );
+ }
+
+ $this->invite = $invite;
+
+ $returnValue = false;
+ if ( $this->api->ends_with( $this->path, '/delete' ) ) {
+ $returnValue = array(
+ 'invite_key' => $invite_id,
+ 'deleted' => $this->delete_invite(),
+ );
+ } else {
+ $returnValue = array(
+ 'result' => $this->is_wpcom ? $this->resend_wpcom_invite() : $this->resend_self_hosted_invite()
+ );
+ }
+
+ return $returnValue;
+ }
+
+ /**
+ * Returns an invite if found or false if not found.
+ *
+ * @return bool|object
+ */
+ function get_invite() {
+ global $wpdb, $wpcom_invite_users;
+
+ $invite = false;
+ if ( $this->is_wpcom ) {
+ $invite = $wpcom_invite_users->get_invitation( $this->invite_id );
+ } else {
+ $query = $wpdb->prepare( "SELECT * FROM $wpdb->options WHERE option_name = %s LIMIT 1", $this->invite_id );
+ $invite = $wpdb->get_results( $query );
+
+ $invite = empty( $invite ) ? false : $invite;
+ }
+
+ return $invite;
+ }
+
+ /**
+ * Deletes an invitation.
+ *
+ * @return bool Whether the invite was deleted successfully.
+ */
+ function delete_invite() {
+ global $wpdb, $wpcom_invite_users;
+
+ if ( $this->is_wpcom ) {
+ return (bool) $wpcom_invite_users->delete_invitation( $this->invite_id );
+ } else {
+ $query = $wpdb->prepare( "DELETE FROM $wpdb->options WHERE option_name = %s", $this->invite_id );
+ return 0 < $wpdb->query( $query );
+ }
+ }
+
+ /**
+ * Sends an invitation email to a user to join a self-hosted site.
+ *
+ * This method duplicates the invitation email functionality that is present
+ * in wp-admin/user-new.php. Ideally, we should factor out the functionality
+ * in wp-admin/user-new.php that actually invites a user and sends the invite
+ * from the data validation checks that expect $_POST and $_REQUEST.
+ *
+ * @return bool Whether the email was sent successfully.
+ */
+ function resend_self_hosted_invite() {
+ $invite = (array) unserialize( $this->invite[0]->option_value );
+ $roles = get_editable_roles();
+ $role = $roles[ $invite['role'] ];
+ $newuser_key = str_replace( 'new_user_', '', $this->invite_id );
+
+ /* translators: 1: Site title 2: Site URL 3: Role name 4: URL to accept invitation */
+ $message = __( 'Hi,
+
+You\'ve been invited to join \'%1$s\' at
+%2$s with the role of %3$s.
+
+Please click the following link to confirm the invite:
+%4$s', 'jetpack' );
+
+ return wp_mail(
+ $invite['email'],
+ sprintf( __( '[%s] Joining confirmation', 'jetpack' ), wp_specialchars_decode( get_option( 'blogname' ) ) ),
+ sprintf(
+ $message,
+ get_option( 'blogname' ),
+ home_url(),
+ wp_specialchars_decode( translate_user_role( $role['name'] ) ),
+ home_url( "/newbloguser/$newuser_key/" )
+ )
+ );
+ }
+
+ /**
+ * Sends an invitation email to a user to join a WordPress.com site.
+ *
+ * @return bool Whether the invitation was sent successfully.
+ */
+ function resend_wpcom_invite() {
+ global $wpcom_invite_users;
+
+ $wpcom_invite_users->update_invitation( $this->invite->invite_slug, array( 'invite_date' => gmdate( 'Y-m-d H:i:s' ) ) );
+
+ if ( 'follower' == $this->invite->meta['role'] && ! is_private_blog() ) {
+ $wpcom_invite_users->invite_followers( $this->invite->meta['sent_to'] );
+ } else {
+ $wpcom_invite_users->send_invitation( $this->invite->invite_slug );
+ }
+
+ return true;
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-media-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-media-v1-1-endpoint.php
index 8a7c8c74..c4a34caf 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-media-v1-1-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-media-v1-1-endpoint.php
@@ -20,23 +20,23 @@ class WPCOM_JSON_API_Update_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
$input = $this->input( true );
$insert = array();
- if ( ! empty( $input['title'] ) ) {
+ if ( isset( $input['title'] ) ) {
$insert['post_title'] = $input['title'];
}
- if ( ! empty( $input['caption'] ) ) {
+ if ( isset( $input['caption'] ) ) {
$insert['post_excerpt'] = $input['caption'];
}
- if ( ! empty( $input['description'] ) ) {
+ if ( isset( $input['description'] ) ) {
$insert['post_content'] = $input['description'];
}
- if ( ! empty( $input['parent_id'] ) ) {
+ if ( isset( $input['parent_id'] ) ) {
$insert['post_parent'] = $input['parent_id'];
}
- if ( ! empty( $input['alt'] ) ) {
+ if ( isset( $input['alt'] ) ) {
$alt = wp_strip_all_tags( $input['alt'], true );
update_post_meta( $media_id, '_wp_attachment_image_alt', $alt );
}
@@ -57,7 +57,7 @@ class WPCOM_JSON_API_Update_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint
);
foreach ( $id3_keys as $key => $label ) {
- if ( ! empty( $input[ $key ] ) ) {
+ if ( isset( $input[ $key ] ) ) {
$changed = true;
$id3data[ $key ] = wp_strip_all_tags( $input[ $key ], true );
}
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-endpoint.php
index 7e5f9dd0..04fae801 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-endpoint.php
@@ -107,16 +107,20 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
return $author_id;
}
- if ( 'publish' === $input['status'] && 'publish' !== $post->post_status && !current_user_can( 'publish_post', $post->ID ) ) {
+ if ( ( isset( $input['status'] ) && 'publish' === $input['status'] ) && 'publish' !== $post->post_status && !current_user_can( 'publish_post', $post->ID ) ) {
$input['status'] = 'pending';
}
$last_status = $post->post_status;
- $new_status = $input['status'];
+ $new_status = isset( $input['status'] ) ? $input['status'] : $last_status;
+
+ // Make sure that drafts get the current date when transitioning to publish if not supplied in the post.
+ $date_in_past = ( strtotime($post->post_date_gmt) < time() );
+ if ( 'publish' === $new_status && 'draft' === $last_status && ! isset( $input['date_gmt'] ) && $date_in_past ) {
+ $input['date_gmt'] = gmdate( 'Y-m-d H:i:s' );
+ }
}
- // Fix for https://iorequests.wordpress.com/2014/08/13/scheduled-posts-made-in-the/
- // See: https://a8c.slack.com/archives/io/p1408047082000273
- // If date was set, $this->input will set date_gmt, date still needs to be adjusted for the blog's offset
+ // If date is set, $this->input will set date_gmt, date still needs to be adjusted for the blog's offset
if ( isset( $input['date_gmt'] ) ) {
$gmt_offset = get_option( 'gmt_offset' );
$time_with_offset = strtotime( $input['date_gmt'] ) + $gmt_offset * HOUR_IN_SECONDS;
@@ -173,7 +177,11 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
}
// only add a new tag/cat if the user has access to
$tax = get_taxonomy( $taxonomy );
- if ( !current_user_can( $tax->cap->edit_terms ) ) {
+
+ // see https://core.trac.wordpress.org/ticket/26409
+ if ( 'category' === $taxonomy && ! current_user_can( $tax->cap->edit_terms ) ) {
+ continue;
+ } else if ( ! current_user_can( $tax->cap->assign_terms ) ) {
continue;
}
@@ -210,42 +218,43 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
unset( $input['slug'] );
}
- if ( true === $input['comments_open'] )
- $insert['comment_status'] = 'open';
- else if ( false === $input['comments_open'] )
- $insert['comment_status'] = 'closed';
+ if ( isset( $input['comments_open'] ) ) {
+ $insert['comment_status'] = ( true === $input['comments_open'] ) ? 'open' : 'closed';
+ }
- if ( true === $input['pings_open'] )
- $insert['ping_status'] = 'open';
- else if ( false === $input['pings_open'] )
- $insert['ping_status'] = 'closed';
+ if ( isset( $input['pings_open'] ) ) {
+ $insert['ping_status'] = ( true === $input['pings_open'] ) ? 'open' : 'closed';
+ }
unset( $input['comments_open'], $input['pings_open'] );
- $insert['menu_order'] = $input['menu_order'];
- unset( $input['menu_order'] );
+ if ( isset( $input['menu_order'] ) ) {
+ $insert['menu_order'] = $input['menu_order'];
+ unset( $input['menu_order'] );
+ }
+
+ $publicize = isset( $input['publicize'] ) ? $input['publicize'] : null;
+ unset( $input['publicize'] );
- $publicize = $input['publicize'];
- $publicize_custom_message = $input['publicize_message'];
- unset( $input['publicize'], $input['publicize_message'] );
+ $publicize_custom_message = isset( $input['publicize_message'] ) ? $input['publicize_message'] : null;
+ unset( $input['publicize_message'] );
if ( isset( $input['featured_image'] ) ) {
$featured_image = trim( $input['featured_image'] );
$delete_featured_image = empty( $featured_image );
- $featured_image = $input['featured_image'];
unset( $input['featured_image'] );
}
- $metadata = $input['metadata'];
+ $metadata = isset( $input['metadata'] ) ? $input['metadata'] : null;
unset( $input['metadata'] );
- $likes = $input['likes_enabled'];
- $sharing = $input['sharing_enabled'];
-
+ $likes = isset( $input['likes_enabled'] ) ? $input['likes_enabled'] : null;
unset( $input['likes_enabled'] );
+
+ $sharing = isset( $input['sharing_enabled'] ) ? $input['sharing_enabled'] : null;
unset( $input['sharing_enabled'] );
- $sticky = $input['sticky'];
+ $sticky = isset( $input['sticky'] ) ? $input['sticky'] : null;
unset( $input['sticky'] );
foreach ( $input as $key => $value ) {
@@ -265,7 +274,7 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
if ( $new ) {
- if ( false === strpos( $input['content'], '[gallery' ) && ( $has_media || $has_media_by_url ) ) {
+ if ( isset( $input['content'] ) && ! has_shortcode( $input['content'], 'gallery' ) && ( $has_media || $has_media_by_url ) ) {
switch ( ( $has_media + $has_media_by_url ) ) {
case 0 :
// No images - do nothing.
@@ -325,6 +334,7 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
}
// Set like status for the post
+ /** This filter is documented in modules/likes.php */
$sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
if ( $new ) {
if ( $sitewide_likes_enabled ) {
@@ -373,17 +383,29 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
}
}
- if ( true === $sticky ) {
- stick_post( $post_id );
- } else {
- unstick_post( $post_id );
+ if ( isset( $sticky ) ) {
+ if ( true === $sticky ) {
+ stick_post( $post_id );
+ } else {
+ unstick_post( $post_id );
+ }
}
// WPCOM Specific (Jetpack's will get bumped elsewhere
- // Tracks how many posts are published and sets meta so we can track some other cool stats (like likes & comments on posts published)
- if ( ( $new && 'publish' == $input['status'] ) || ( !$new && isset( $last_status ) && 'publish' != $last_status && isset( $new_status ) && 'publish' == $new_status ) ) {
- if ( function_exists( 'bump_stats_extras' ) ) {
- bump_stats_extras( 'api-insights-posts', $this->api->token_details['client_id'] );
+ // Tracks how many posts are published and sets meta
+ // so we can track some other cool stats (like likes & comments on posts published)
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ if (
+ ( $new && 'publish' == $input['status'] )
+ || (
+ ! $new && isset( $last_status )
+ && 'publish' != $last_status
+ && isset( $new_status )
+ && 'publish' == $new_status
+ )
+ ) {
+ /** This action is documented in modules/widgets/social-media-icons.php */
+ do_action( 'jetpack_bump_stats_extras', 'api-insights-posts', $this->api->token_details['client_id'] );
update_post_meta( $post_id, '_rest_api_published', 1 );
update_post_meta( $post_id, '_rest_api_client_id', $this->api->token_details['client_id'] );
}
@@ -393,7 +415,7 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
// We ask the user/dev to pass Publicize services he/she wants activated for the post, but Publicize expects us
// to instead flag the ones we don't want to be skipped. proceed with said logic.
// any posts coming from Path (client ID 25952) should also not publicize
- if ( $publicize === false || 25952 == $this->api->token_details['client_id'] ) {
+ if ( $publicize === false || ( isset( $this->api->token_details['client_id'] ) && 25952 == $this->api->token_details['client_id'] ) ) {
// No publicize at all, skip all by ID
foreach ( $GLOBALS['publicize_ui']->publicize->get_services( 'all' ) as $name => $service ) {
delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $name );
@@ -459,10 +481,22 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
}
}
- if ( !empty( $publicize_custom_message ) )
- update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS, trim( $publicize_custom_message ) );
+ if ( ! is_null( $publicize_custom_message ) ) {
+ if ( empty( $publicize_custom_message ) ) {
+ delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS );
+ } else {
+ update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS, trim( $publicize_custom_message ) );
+ }
+ }
- set_post_format( $post_id, $insert['post_format'] );
+ if ( ! empty( $insert['post_format'] ) ) {
+ if ( 'default' !== strtolower( $insert['post_format'] ) ) {
+ set_post_format( $post_id, $insert['post_format'] );
+ }
+ else {
+ set_post_format( $post_id, get_option( 'default_post_format' ) );
+ }
+ }
if ( isset( $featured_image ) ) {
$this->parse_and_set_featured_image( $post_id, $delete_featured_image, $featured_image );
@@ -542,6 +576,17 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
}
}
+ /**
+ * Fires when a post is created via the REST API.
+ *
+ * @module json-api
+ *
+ * @since 2.3.0
+ *
+ * @param int $post_id Post ID.
+ * @param array $insert Data used to build the post.
+ * @param string $new New post URL suffix.
+ */
do_action( 'rest_api_inserted_post', $post_id, $insert, $new );
$return = $this->get_post_by( 'ID', $post_id, $args['context'] );
@@ -549,13 +594,16 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
return $return;
}
- if ( 'revision' === $input['type'] ) {
+ if ( isset( $input['type'] ) && 'revision' === $input['type'] ) {
$return['preview_nonce'] = wp_create_nonce( 'post_preview_' . $input['parent'] );
}
- // workaround for sticky test occasionally failing, maybe a race condition with stick_post() above
- $return['sticky'] = ( true === $sticky );
+ if ( isset( $sticky ) ) {
+ // workaround for sticky test occasionally failing, maybe a race condition with stick_post() above
+ $return['sticky'] = ( true === $sticky );
+ }
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
return $return;
@@ -582,6 +630,7 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
wp_delete_post( $post->ID );
@@ -608,6 +657,7 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint {
return new WP_Error( 'unauthorized', 'User cannot restore trashed posts', 403 );
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
wp_untrash_post( $post->ID );
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-1-endpoint.php
index 7b4cf2e7..d0aef8bf 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-1-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-1-endpoint.php
@@ -38,6 +38,11 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
add_action( 'rest_api_inserted_post', array( $GLOBALS['publicize_ui']->publicize, 'async_publicize_post' ) );
}
+ // 'future' is an alias for 'publish' for now
+ if ( 'future' === $input['status'] ) {
+ $input['status'] = 'publish';
+ }
+
if ( $new ) {
$input = $this->input( true );
@@ -107,16 +112,20 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
return $author_id;
}
- if ( 'publish' === $input['status'] && 'publish' !== $post->post_status && !current_user_can( 'publish_post', $post->ID ) ) {
+ if ( ( isset( $input['status'] ) && 'publish' === $input['status'] ) && 'publish' !== $post->post_status && !current_user_can( 'publish_post', $post->ID ) ) {
$input['status'] = 'pending';
}
$last_status = $post->post_status;
- $new_status = $input['status'];
+ $new_status = isset( $input['status'] ) ? $input['status'] : $last_status;
+
+ // Make sure that drafts get the current date when transitioning to publish if not supplied in the post.
+ $date_in_past = ( strtotime($post->post_date_gmt) < time() );
+ if ( 'publish' === $new_status && 'draft' === $last_status && ! isset( $input['date_gmt'] ) && $date_in_past ) {
+ $input['date_gmt'] = gmdate( 'Y-m-d H:i:s' );
+ }
}
- // Fix for https://iorequests.wordpress.com/2014/08/13/scheduled-posts-made-in-the/
- // See: https://a8c.slack.com/archives/io/p1408047082000273
- // If date was set, $this->input will set date_gmt, date still needs to be adjusted for the blog's offset
+ // If date is set, $this->input will set date_gmt, date still needs to be adjusted for the blog's offset
if ( isset( $input['date_gmt'] ) ) {
$gmt_offset = get_option( 'gmt_offset' );
$time_with_offset = strtotime( $input['date_gmt'] ) + $gmt_offset * HOUR_IN_SECONDS;
@@ -173,7 +182,11 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
}
// only add a new tag/cat if the user has access to
$tax = get_taxonomy( $taxonomy );
- if ( !current_user_can( $tax->cap->edit_terms ) ) {
+
+ // see https://core.trac.wordpress.org/ticket/26409
+ if ( 'category' === $taxonomy && ! current_user_can( $tax->cap->edit_terms ) ) {
+ continue;
+ } else if ( ! current_user_can( $tax->cap->assign_terms ) ) {
continue;
}
@@ -229,12 +242,16 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
unset( $input['discussion'] );
- $insert['menu_order'] = $input['menu_order'];
- unset( $input['menu_order'] );
+ if ( isset( $input['menu_order'] ) ) {
+ $insert['menu_order'] = $input['menu_order'];
+ unset( $input['menu_order'] );
+ }
+
+ $publicize = isset( $input['publicize'] ) ? $input['publicize'] : null;
+ unset( $input['publicize'] );
- $publicize = $input['publicize'];
- $publicize_custom_message = $input['publicize_message'];
- unset( $input['publicize'], $input['publicize_message'] );
+ $publicize_custom_message = isset( $input['publicize_message'] ) ? $input['publicize_message'] : null;
+ unset( $input['publicize_message'] );
if ( isset( $input['featured_image'] ) ) {
$featured_image = trim( $input['featured_image'] );
@@ -242,16 +259,16 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
unset( $input['featured_image'] );
}
- $metadata = $input['metadata'];
+ $metadata = isset( $input['metadata'] ) ? $input['metadata'] : null;
unset( $input['metadata'] );
- $likes = $input['likes_enabled'];
- $sharing = $input['sharing_enabled'];
-
+ $likes = isset( $input['likes_enabled'] ) ? $input['likes_enabled'] : null;
unset( $input['likes_enabled'] );
+
+ $sharing = isset( $input['sharing_enabled'] ) ? $input['sharing_enabled'] : null;
unset( $input['sharing_enabled'] );
- $sticky = $input['sticky'];
+ $sticky = isset( $input['sticky'] ) ? $input['sticky'] : null;
unset( $input['sticky'] );
foreach ( $input as $key => $value ) {
@@ -271,7 +288,7 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
if ( $new ) {
- if ( false === strpos( $input['content'], '[gallery' ) && ( $has_media || $has_media_by_url ) ) {
+ if ( isset( $input['content'] ) && ! has_shortcode( $input['content'], 'gallery' ) && ( $has_media || $has_media_by_url ) ) {
switch ( ( $has_media + $has_media_by_url ) ) {
case 0 :
// No images - do nothing.
@@ -330,6 +347,7 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
}
// Set like status for the post
+ /** This filter is documented in modules/likes.php */
$sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
if ( $new ) {
if ( $sitewide_likes_enabled ) {
@@ -378,17 +396,29 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
}
}
- if ( true === $sticky ) {
- stick_post( $post_id );
- } else {
- unstick_post( $post_id );
+ if ( isset( $sticky ) ) {
+ if ( true === $sticky ) {
+ stick_post( $post_id );
+ } else {
+ unstick_post( $post_id );
+ }
}
// WPCOM Specific (Jetpack's will get bumped elsewhere
- // Tracks how many posts are published and sets meta so we can track some other cool stats (like likes & comments on posts published)
- if ( ( $new && 'publish' == $input['status'] ) || ( !$new && isset( $last_status ) && 'publish' != $last_status && isset( $new_status ) && 'publish' == $new_status ) ) {
- if ( function_exists( 'bump_stats_extras' ) ) {
- bump_stats_extras( 'api-insights-posts', $this->api->token_details['client_id'] );
+ // Tracks how many posts are published and sets meta
+ // so we can track some other cool stats (like likes & comments on posts published)
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ if (
+ ( $new && 'publish' == $input['status'] )
+ || (
+ ! $new && isset( $last_status )
+ && 'publish' != $last_status
+ && isset( $new_status )
+ && 'publish' == $new_status
+ )
+ ) {
+ /** This action is documented in modules/widgets/social-media-icons.php */
+ do_action( 'jetpack_bump_stats_extras', 'api-insights-posts', $this->api->token_details['client_id'] );
update_post_meta( $post_id, '_rest_api_published', 1 );
update_post_meta( $post_id, '_rest_api_client_id', $this->api->token_details['client_id'] );
}
@@ -398,7 +428,7 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
// We ask the user/dev to pass Publicize services he/she wants activated for the post, but Publicize expects us
// to instead flag the ones we don't want to be skipped. proceed with said logic.
// any posts coming from Path (client ID 25952) should also not publicize
- if ( $publicize === false || 25952 == $this->api->token_details['client_id'] ) {
+ if ( $publicize === false || ( isset( $this->api->token_details['client_id'] ) && 25952 == $this->api->token_details['client_id'] ) ) {
// No publicize at all, skip all by ID
foreach ( $GLOBALS['publicize_ui']->publicize->get_services( 'all' ) as $name => $service ) {
delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $name );
@@ -465,10 +495,22 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
}
}
- if ( !empty( $publicize_custom_message ) )
- update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS, trim( $publicize_custom_message ) );
+ if ( ! is_null( $publicize_custom_message ) ) {
+ if ( empty( $publicize_custom_message ) ) {
+ delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS );
+ } else {
+ update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS, trim( $publicize_custom_message ) );
+ }
+ }
- set_post_format( $post_id, $insert['post_format'] );
+ if ( ! empty( $insert['post_format'] ) ) {
+ if ( 'default' !== strtolower( $insert['post_format'] ) ) {
+ set_post_format( $post_id, $insert['post_format'] );
+ }
+ else {
+ set_post_format( $post_id, get_option( 'default_post_format' ) );
+ }
+ }
if ( isset( $featured_image ) ) {
$this->parse_and_set_featured_image( $post_id, $delete_featured_image, $featured_image );
@@ -548,6 +590,7 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
}
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-update-post-endpoint.php */
do_action( 'rest_api_inserted_post', $post_id, $insert, $new );
$return = $this->get_post_by( 'ID', $post_id, $args['context'] );
@@ -555,16 +598,23 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
return $return;
}
- if ( 'revision' === $input['type'] ) {
+ if ( isset( $input['type'] ) && 'revision' === $input['type'] ) {
$return['preview_nonce'] = wp_create_nonce( 'post_preview_' . $input['parent'] );
}
- // workaround for sticky test occasionally failing, maybe a race condition with stick_post() above
- $return['sticky'] = ( true === $sticky );
+ if ( isset( $sticky ) ) {
+ // workaround for sticky test occasionally failing, maybe a race condition with stick_post() above
+ $return['sticky'] = ( true === $sticky );
+ }
if ( ! empty( $media_results['errors'] ) )
$return['media_errors'] = $media_results['errors'];
+ if ( 'publish' !== $post->post_status ) {
+ $return['other_URLs'] = (object) $this->get_post_permalink_suggestions( $post_id, $input['title'] );
+ }
+
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
return $return;
@@ -591,6 +641,7 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
wp_delete_post( $post->ID );
@@ -617,6 +668,7 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_
return new WP_Error( 'unauthorized', 'User cannot restore trashed posts', 403 );
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
wp_untrash_post( $post->ID );
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-2-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-2-endpoint.php
index 2d50168b..22838146 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-2-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-post-v1-2-endpoint.php
@@ -13,6 +13,11 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
add_action( 'rest_api_inserted_post', array( $GLOBALS['publicize_ui']->publicize, 'async_publicize_post' ) );
}
+ // 'future' is an alias for 'publish' for now
+ if ( isset( $input['status'] ) && 'future' === $input['status'] ) {
+ $input['status'] = 'publish';
+ }
+
if ( $new ) {
$input = $this->input( true );
@@ -82,16 +87,20 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
return $author_id;
}
- if ( 'publish' === $input['status'] && 'publish' !== $post->post_status && !current_user_can( 'publish_post', $post->ID ) ) {
+ if ( ( isset( $input['status'] ) && 'publish' === $input['status'] ) && 'publish' !== $post->post_status && !current_user_can( 'publish_post', $post->ID ) ) {
$input['status'] = 'pending';
}
$last_status = $post->post_status;
- $new_status = $input['status'];
+ $new_status = isset( $input['status'] ) ? $input['status'] : $last_status;
+
+ // Make sure that drafts get the current date when transitioning to publish if not supplied in the post.
+ $date_in_past = ( strtotime($post->post_date_gmt) < time() );
+ if ( 'publish' === $new_status && 'draft' === $last_status && ! isset( $input['date_gmt'] ) && $date_in_past ) {
+ $input['date_gmt'] = gmdate( 'Y-m-d H:i:s' );
+ }
}
- // Fix for https://iorequests.wordpress.com/2014/08/13/scheduled-posts-made-in-the/
- // See: https://a8c.slack.com/archives/io/p1408047082000273
- // If date was set, $this->input will set date_gmt, date still needs to be adjusted for the blog's offset
+ // If date is set, $this->input will set date_gmt, date still needs to be adjusted for the blog's offset
if ( isset( $input['date_gmt'] ) ) {
$gmt_offset = get_option( 'gmt_offset' );
$time_with_offset = strtotime( $input['date_gmt'] ) + $gmt_offset * HOUR_IN_SECONDS;
@@ -138,7 +147,11 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
if ( ! $term_info ) {
// only add a new tag/cat if the user has access to
$tax = get_taxonomy( $taxonomy );
- if ( ! current_user_can( $tax->cap->edit_terms ) ) {
+
+ // see https://core.trac.wordpress.org/ticket/26409
+ if ( 'category' === $taxonomy && ! current_user_can( $tax->cap->edit_terms ) ) {
+ continue;
+ } else if ( ! current_user_can( $tax->cap->assign_terms ) ) {
continue;
}
@@ -164,7 +177,7 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
}
// combine with any previous selections
- if ( ! is_array( $tax_input[ $taxonomy ] ) ) {
+ if ( ! isset( $tax_input[ $taxonomy ] ) || ! is_array( $tax_input[ $taxonomy ] ) ) {
$tax_input[ $taxonomy ] = array();
}
@@ -229,12 +242,16 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
unset( $input['discussion'] );
- $insert['menu_order'] = $input['menu_order'];
- unset( $input['menu_order'] );
+ if ( isset( $input['menu_order'] ) ) {
+ $insert['menu_order'] = $input['menu_order'];
+ unset( $input['menu_order'] );
+ }
+
+ $publicize = isset( $input['publicize'] ) ? $input['publicize'] : null;
+ unset( $input['publicize'] );
- $publicize = $input['publicize'];
- $publicize_custom_message = $input['publicize_message'];
- unset( $input['publicize'], $input['publicize_message'] );
+ $publicize_custom_message = isset( $input['publicize_message'] ) ? $input['publicize_message'] : null;
+ unset( $input['publicize_message'] );
if ( isset( $input['featured_image'] ) ) {
$featured_image = trim( $input['featured_image'] );
@@ -242,16 +259,16 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
unset( $input['featured_image'] );
}
- $metadata = $input['metadata'];
+ $metadata = isset( $input['metadata'] ) ? $input['metadata'] : null;
unset( $input['metadata'] );
- $likes = $input['likes_enabled'];
- $sharing = $input['sharing_enabled'];
-
+ $likes = isset( $input['likes_enabled'] ) ? $input['likes_enabled'] : null;
unset( $input['likes_enabled'] );
+
+ $sharing = isset( $input['sharing_enabled'] ) ? $input['sharing_enabled'] : null;
unset( $input['sharing_enabled'] );
- $sticky = $input['sticky'];
+ $sticky = isset( $input['sticky'] ) ? $input['sticky'] : null;
unset( $input['sticky'] );
foreach ( $input as $key => $value ) {
@@ -271,7 +288,7 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
if ( $new ) {
- if ( false === strpos( $input['content'], '[gallery' ) && ( $has_media || $has_media_by_url ) ) {
+ if ( isset( $input['content'] ) && ! has_shortcode( $input['content'], 'gallery' ) && ( $has_media || $has_media_by_url ) ) {
switch ( ( $has_media + $has_media_by_url ) ) {
case 0 :
// No images - do nothing.
@@ -330,6 +347,7 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
}
// Set like status for the post
+ /** This filter is documented in modules/likes.php */
$sitewide_likes_enabled = (bool) apply_filters( 'wpl_is_enabled_sitewide', ! get_option( 'disabled_likes' ) );
if ( $new ) {
if ( $sitewide_likes_enabled ) {
@@ -378,17 +396,29 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
}
}
- if ( true === $sticky ) {
- stick_post( $post_id );
- } else {
- unstick_post( $post_id );
+ if ( isset( $sticky ) ) {
+ if ( true === $sticky ) {
+ stick_post( $post_id );
+ } else {
+ unstick_post( $post_id );
+ }
}
// WPCOM Specific (Jetpack's will get bumped elsewhere
- // Tracks how many posts are published and sets meta so we can track some other cool stats (like likes & comments on posts published)
- if ( ( $new && 'publish' == $input['status'] ) || ( !$new && isset( $last_status ) && 'publish' != $last_status && isset( $new_status ) && 'publish' == $new_status ) ) {
- if ( function_exists( 'bump_stats_extras' ) ) {
- bump_stats_extras( 'api-insights-posts', $this->api->token_details['client_id'] );
+ // Tracks how many posts are published and sets meta
+ // so we can track some other cool stats (like likes & comments on posts published)
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ if (
+ ( $new && 'publish' == $input['status'] )
+ || (
+ !$new && isset( $last_status )
+ && 'publish' != $last_status
+ && isset( $new_status )
+ && 'publish' == $new_status
+ )
+ ) {
+ /** This action is documented in modules/widgets/social-media-icons.php */
+ do_action( 'jetpack_bump_stats_extras', 'api-insights-posts', $this->api->token_details['client_id'] );
update_post_meta( $post_id, '_rest_api_published', 1 );
update_post_meta( $post_id, '_rest_api_client_id', $this->api->token_details['client_id'] );
}
@@ -398,7 +428,7 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
// We ask the user/dev to pass Publicize services he/she wants activated for the post, but Publicize expects us
// to instead flag the ones we don't want to be skipped. proceed with said logic.
// any posts coming from Path (client ID 25952) should also not publicize
- if ( $publicize === false || 25952 == $this->api->token_details['client_id'] ) {
+ if ( $publicize === false || ( isset( $this->api->token_details['client_id'] ) && 25952 == $this->api->token_details['client_id'] ) ) {
// No publicize at all, skip all by ID
foreach ( $GLOBALS['publicize_ui']->publicize->get_services( 'all' ) as $name => $service ) {
delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_SKIP . $name );
@@ -465,10 +495,22 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
}
}
- if ( !empty( $publicize_custom_message ) )
- update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS, trim( $publicize_custom_message ) );
+ if ( ! is_null( $publicize_custom_message ) ) {
+ if ( empty( $publicize_custom_message ) ) {
+ delete_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS );
+ } else {
+ update_post_meta( $post_id, $GLOBALS['publicize_ui']->publicize->POST_MESS, trim( $publicize_custom_message ) );
+ }
+ }
- set_post_format( $post_id, $insert['post_format'] );
+ if ( ! empty( $insert['post_format'] ) ) {
+ if ( 'default' !== strtolower( $insert['post_format'] ) ) {
+ set_post_format( $post_id, $insert['post_format'] );
+ }
+ else {
+ set_post_format( $post_id, get_option( 'default_post_format' ) );
+ }
+ }
if ( isset( $featured_image ) ) {
parent::parse_and_set_featured_image( $post_id, $delete_featured_image, $featured_image );
@@ -548,6 +590,7 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
}
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-update-post-endpoint.php */
do_action( 'rest_api_inserted_post', $post_id, $insert, $new );
$return = $this->get_post_by( 'ID', $post_id, $args['context'] );
@@ -555,16 +598,23 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos
return $return;
}
- if ( 'revision' === $input['type'] ) {
+ if ( isset( $input['type'] ) && 'revision' === $input['type'] ) {
$return['preview_nonce'] = wp_create_nonce( 'post_preview_' . $input['parent'] );
}
- // workaround for sticky test occasionally failing, maybe a race condition with stick_post() above
- $return['sticky'] = ( true === $sticky );
+ if ( isset( $sticky ) ) {
+ // workaround for sticky test occasionally failing, maybe a race condition with stick_post() above
+ $return['sticky'] = ( true === $sticky );
+ }
if ( ! empty( $media_results['errors'] ) )
$return['media_errors'] = $media_results['errors'];
+ if ( 'publish' !== $post->post_status && isset( $input['title'] )) {
+ $return['other_URLs'] = (object) $this->get_post_permalink_suggestions( $post_id, $input['title'] );
+ }
+
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'posts' );
return $return;
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-taxonomy-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-taxonomy-endpoint.php
index 850eb14b..3c3ae564 100644
--- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-taxonomy-endpoint.php
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-taxonomy-endpoint.php
@@ -43,13 +43,17 @@ class WPCOM_JSON_API_Update_Taxonomy_Endpoint extends WPCOM_JSON_API_Taxonomy_En
return new WP_Error( 'unauthorized', 'User cannot edit taxonomy', 403 );
}
- if ( term_exists( $input['name'], $taxonomy_type ) ) {
- return new WP_Error( 'duplicate', 'A taxonomy with that name already exists', 400 );
- }
-
- if ( 'category' !== $taxonomy_type )
+ if ( 'category' !== $taxonomy_type || ! isset( $input['parent'] ) )
$input['parent'] = 0;
+ if ( $term = get_term_by( 'name', $input['name'], $taxonomy_type ) ) {
+ // get_term_by is not case-sensitive, but a name with different casing is allowed
+ // also, the exact same name is allowed as long as the parents are different
+ if ( $input['name'] === $term->name && $input['parent'] === $term->parent ) {
+ return new WP_Error( 'duplicate', 'A taxonomy with that name already exists', 400 );
+ }
+ }
+
$data = wp_insert_term( addslashes( $input['name'] ), $taxonomy_type,
array(
'description' => addslashes( $input['description'] ),
@@ -67,6 +71,7 @@ class WPCOM_JSON_API_Update_Taxonomy_Endpoint extends WPCOM_JSON_API_Taxonomy_En
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'taxonomies' );
return $return;
}
@@ -111,6 +116,7 @@ class WPCOM_JSON_API_Update_Taxonomy_Endpoint extends WPCOM_JSON_API_Taxonomy_En
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'taxonomies' );
return $return;
}
@@ -136,6 +142,7 @@ class WPCOM_JSON_API_Update_Taxonomy_Endpoint extends WPCOM_JSON_API_Taxonomy_En
return $return;
}
+ /** This action is documented in json-endpoints/class.wpcom-json-api-site-settings-endpoint.php */
do_action( 'wpcom_json_api_objects', 'taxonomies' );
wp_delete_term( $taxonomy->term_id, $taxonomy_type );
diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-user-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-user-endpoint.php
new file mode 100644
index 00000000..f5915aff
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-user-endpoint.php
@@ -0,0 +1,101 @@
+<?php
+
+class WPCOM_JSON_API_Update_User_Endpoint extends WPCOM_JSON_API_Endpoint {
+
+ function callback( $path = '', $blog_id = 0, $user_id = 0 ) {
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ if ( wpcom_get_blog_owner( $blog_id ) == $user_id ) {
+ return new WP_Error( 'forbidden', 'A site owner can not be removed through this endpoint.', 403 );
+ }
+ }
+
+ if ( $this->api->ends_with( $path, '/delete' ) ) {
+ return $this->delete_or_remove_user( $user_id );
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if a user exists by checking to see if a WP_User object exists for a user ID.
+ * @param int $user_id
+ * @return bool
+ */
+ function user_exists( $user_id ) {
+ $user = get_user_by( 'id', $user_id );
+
+ return false != $user && is_a( $user, 'WP_User' );
+ }
+
+ /**
+ * Validates user input and then decides whether to remove or delete a user.
+ * @param int $user_id
+ * @return array|WP_Error
+ */
+ function delete_or_remove_user( $user_id ) {
+ if ( 0 == $user_id ) {
+ return new WP_Error( 'invalid_input', 'A valid user ID must be specified.', 400 );
+ }
+
+ if ( get_current_user_id() == $user_id ) {
+ return new WP_Error( 'invalid_input', 'User can not remove or delete self through this endpoint.', 400 );
+ }
+
+ if ( ! $this->user_exists( $user_id ) ) {
+ return new WP_Error( 'invalid_input', 'A user does not exist with that ID.', 400 );
+ }
+
+ return is_multisite() ? $this->remove_user( $user_id ) : $this->delete_user( $user_id );
+ }
+
+ /**
+ * Removes a user from the current site.
+ * @param int $user_id
+ * @return array|WP_Error
+ */
+ function remove_user( $user_id ) {
+ if ( ! current_user_can( 'remove_users' ) ) {
+ return new WP_Error( 'unauthorized', 'User cannot remove users for specified site.', 403 );
+ }
+
+ if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
+ return new WP_Error( 'invalid_input', 'User is not a member of the specified site.', 400 );
+ }
+
+ return array(
+ 'success' => remove_user_from_blog( $user_id, get_current_blog_id() )
+ );
+ }
+
+ /**
+ * Deletes a user and optionally reassigns posts to another user.
+ * @param int $user_id
+ * @return array|WP_Error
+ */
+ function delete_user( $user_id ) {
+ if ( ! current_user_can( 'delete_users' ) ) {
+ return new WP_Error( 'unauthorized', 'User cannot delete users for specified site.', 403 );
+ }
+
+ $input = (array) $this->input();
+
+ if ( isset( $input['reassign'] ) ) {
+ if ( $user_id == $input['reassign'] ) {
+ return new WP_Error( 'invalid_input', 'Can not reassign posts to user being deleted.', 400 );
+ }
+
+ if ( ! $this->user_exists( $input['reassign'] ) ) {
+ return new WP_Error( 'invalid_input', 'User specified in reassign argument is not a member of the specified site.', 400 );
+ }
+ }
+
+ return array(
+ 'success' => wp_delete_user( $user_id, intval( $input['reassign'] ) ),
+ );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-log-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-log-endpoint.php
new file mode 100644
index 00000000..f1d0f4da
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-log-endpoint.php
@@ -0,0 +1,16 @@
+<?php
+
+class Jetpack_JSON_API_Jetpack_Log_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // GET /sites/%s/jetpack-log
+ protected $needed_capabilities = 'manage_options';
+
+ protected function result() {
+ $args = $this->input();
+ $event = ( isset( $args['event'] ) && is_string( $args['event'] ) ) ? $code : false;
+ $num = ( isset( $args['num'] ) ) ? intval( $num ) : false;
+
+ return array(
+ 'log' => Jetpack::get_log( $event, $num )
+ );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-maybe-auto-update-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-maybe-auto-update-endpoint.php
new file mode 100644
index 00000000..5b368760
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-maybe-auto-update-endpoint.php
@@ -0,0 +1,32 @@
+<?php
+
+class Jetpack_JSON_API_Maybe_Auto_Update_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // POST /sites/%s/maybe_auto_update
+ protected $needed_capabilities = array( 'update_core', 'update_plugins', 'update_themes' );
+
+ protected $update_results = array();
+
+ protected function result() {
+ add_action( 'automatic_updates_complete', array( $this, 'get_update_results' ), 100, 1 );
+
+ wp_maybe_auto_update();
+
+ $result['log'] = $this->update_results;
+
+ if ( empty( $result['log'] ) ) {
+ $possible_reasons_for_failure = Jetpack_Autoupdate::get_possible_failures();
+
+ if ( $possible_reasons_for_failure ) {
+ $result['log']['error'] = $possible_reasons_for_failure;
+ }
+
+ }
+
+ return $result;
+ }
+
+ public function get_update_results( $results ) {
+ $this->update_results = $results;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php
index 0d9d54a8..79b637b3 100644
--- a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php
@@ -93,11 +93,8 @@ abstract class Jetpack_JSON_API_Modules_Endpoint extends Jetpack_JSON_API_Endpoi
// Fetch the HTML formatted long description
ob_start();
- if ( Jetpack::is_active() && has_action( 'jetpack_module_more_info_connected_' . $module_slug ) ) {
- do_action( 'jetpack_module_more_info_connected_' . $module_slug );
- } else {
- do_action( 'jetpack_module_more_info_' . $module_slug );
- }
+ /** This action is documented in class.jetpack-modules-list-table.php */
+ do_action( 'jetpack_module_more_info_' . $module_slug );
$module['description'] = ob_get_clean();
return $module;
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php
index 9fb4fc28..1b546471 100644
--- a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php
@@ -27,6 +27,7 @@ abstract class Jetpack_JSON_API_Plugins_Endpoint extends Jetpack_JSON_API_Endpoi
'autoupdate' => '(boolean) Whether the plugin is automatically updated',
'next_autoupdate' => '(string) Y-m-d H:i:s for next scheduled update event',
'log' => '(array:safehtml) An array of update log strings.',
+ 'uninstallable' => '(boolean) Whether the plugin is unistallable.',
);
protected function result() {
@@ -110,6 +111,7 @@ abstract class Jetpack_JSON_API_Plugins_Endpoint extends Jetpack_JSON_API_Endpoi
$plugin['update'] = $this->get_plugin_updates( $plugin_file );
$plugin['next_autoupdate'] = date( 'Y-m-d H:i:s', wp_next_scheduled( 'wp_maybe_auto_update' ) );
$plugin['autoupdate'] = in_array( $plugin_file, Jetpack_Options::get_option( 'autoupdate_plugins', array() ) );
+ $plugin['uninstallable'] = is_uninstallable_plugin( $plugin_file );
if ( ! empty ( $this->log[ $plugin_file ] ) ) {
$plugin['log'] = $this->log[ $plugin_file ];
}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php
index 081a5add..110c257f 100644
--- a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php
@@ -5,38 +5,46 @@ include_once ABSPATH . 'wp-admin/includes/file.php';
class Jetpack_JSON_API_Plugins_Install_Endpoint extends Jetpack_JSON_API_Plugins_Endpoint {
- // POST /sites/%s/plugins/%s/new
+ // POST /sites/%s/plugins/%s/install
protected $needed_capabilities = 'install_plugins';
protected $action = 'install';
- protected $download_links = array();
protected function install() {
foreach ( $this->plugins as $index => $slug ) {
- $skin = new Automatic_Upgrader_Skin();
+ $skin = new Jetpack_Automatic_Plugin_Install_Skin();
$upgrader = new Plugin_Upgrader( $skin );
+ $zip_url = self::generate_wordpress_org_plugin_download_link( $slug );
- $result = $upgrader->install( $this->download_links[ $slug ] );
+ $result = $upgrader->install( $zip_url );
if ( ! $this->bulk && is_wp_error( $result ) ) {
return $result;
}
$plugin = self::get_plugin_id_by_slug( $slug );
-
+ $error_code = 'install_error';
if ( ! $plugin ) {
$error = $this->log[ $slug ]['error'] = __( 'There was an error installing your plugin', 'jetpack' );
}
if ( ! $this->bulk && ! $result ) {
- $error = $this->log[ $slug ]['error'] = __( 'An unknown error occurred during installation', 'jetpack' );
+ $error_code = $upgrader->skin->get_main_error_code();
+ $message = $upgrader->skin->get_main_error_message();
+ $error = $this->log[ $slug ]['error'] = $message ? $message : __( 'An unknown error occurred during installation' , 'jetpack' );
}
$this->log[ $plugin ][] = $upgrader->skin->get_upgrade_messages();
}
if ( ! $this->bulk && isset( $error ) ) {
- return new WP_Error( 'install_error', $this->log[ $slug ]['error'], 400 );
+
+ if ( 'download_failed' === $error_code ) {
+ // For backwards compatibility: versions prior to 3.9 would return no_package instead of download_failed.
+ $error_code = 'no_package';
+ }
+
+ return new WP_Error( $error_code, $this->log[ $slug ]['error'], 400 );
}
// replace the slug with the actual plugin id
@@ -50,34 +58,141 @@ class Jetpack_JSON_API_Plugins_Install_Endpoint extends Jetpack_JSON_API_Plugins
return new WP_Error( 'missing_plugins', __( 'No plugins found.', 'jetpack' ) );
}
foreach( $this->plugins as $index => $slug ) {
-
// make sure it is not already installed
if ( self::get_plugin_id_by_slug( $slug ) ) {
return new WP_Error( 'plugin_already_installed', __( 'The plugin is already installed', 'jetpack' ) );
}
- $response = wp_remote_get( "http://api.wordpress.org/plugins/info/1.0/$slug" );
- $plugin_data = unserialize( $response['body'] );
- if ( is_wp_error( $plugin_data ) ) {
- return $plugin_data;
- }
-
- $this->download_links[ $slug ] = $plugin_data->download_link;
-
}
return true;
}
+ protected static function generate_wordpress_org_plugin_download_link( $plugin_slug ) {
+ return "https://downloads.wordpress.org/plugin/{$plugin_slug}.latest-stable.zip";
+ }
+
protected static function get_plugin_id_by_slug( $slug ) {
$plugins = get_plugins();
- if( ! is_array( $plugins ) ) {
+ if ( ! is_array( $plugins ) ) {
return false;
}
- foreach( $plugins as $id => $plugin_data ) {
- if( strpos( $id, $slug ) !== false ) {
- return $id;
+ foreach( $plugins as $plugin_file => $plugin_data ) {
+ if ( self::get_slug_from_file_path( $plugin_file ) === $slug ) {
+ return $plugin_file;
}
}
return false;
}
+
+ protected static function get_slug_from_file_path( $plugin_file ) {
+ // Simular to get_plugin_slug() method.
+ $slug = dirname( $plugin_file );
+ if ( '.' === $slug ) {
+ $slug = preg_replace("/(.+)\.php$/", "$1", $plugin_file );
+ }
+ return $slug;
+ }
+}
+/**
+ * Allows us to capture that the site doesn't have proper file system access.
+ * In order to update the plugin.
+ */
+class Jetpack_Automatic_Plugin_Install_Skin extends Automatic_Upgrader_Skin {
+ /**
+ * Stores the last error key;
+ **/
+ protected $main_error_code = 'install_error';
+
+ /**
+ * Stores the last error message.
+ **/
+ protected $main_error_message = 'An unknown error occurred during installation';
+
+ /**
+ * Overwrites the set_upgrader to be able to tell if we e ven have the ability to write to the files.
+ *
+ * @param WP_Upgrader $upgrader
+ *
+ */
+ public function set_upgrader( &$upgrader ) {
+ parent::set_upgrader( $upgrader );
+
+ // Check if we even have permission to.
+ $result = $upgrader->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
+ if ( ! $result ) {
+ // set the string here since they are not available just yet
+ $upgrader->generic_strings();
+ $this->feedback( 'fs_unavailable' );
+ }
+ }
+
+ /**
+ * Overwrites the error function
+ */
+ public function error( $error ) {
+ if ( is_wp_error( $error ) ) {
+ $this->feedback( $error );
+ }
+ }
+
+ private function set_main_error_code( $code ) {
+ // Don't set the process_failed as code since it is not that helpful unless we don't have one already set.
+ $this->main_error_code = ( $code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $code );
+ }
+
+ private function set_main_error_message( $message, $code ) {
+ // Don't set the process_failed as message since it is not that helpful unless we don't have one already set.
+ $this->main_error_message = ( $code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $message );
+ }
+
+ public function get_main_error_code() {
+ return $this->main_error_code;
+ }
+
+ public function get_main_error_message() {
+ return $this->main_error_message;
+ }
+
+ /**
+ * Overwrites the feedback function
+ */
+ public function feedback( $data ) {
+
+ $current_error = null;
+ if ( is_wp_error( $data ) ) {
+ $this->set_main_error_code( $data->get_error_code() );
+ $string = $data->get_error_message();
+ } elseif ( is_array( $data ) ) {
+ return;
+ } else {
+ $string = $data;
+ }
+
+ if ( ! empty( $this->upgrader->strings[ $string ] ) ) {
+ $this->set_main_error_code( $string );
+
+ $current_error = $string;
+ $string = $this->upgrader->strings[ $string ];
+ }
+
+ if ( strpos( $string, '%' ) !== false ) {
+ $args = func_get_args();
+ $args = array_splice( $args, 1 );
+ if ( ! empty( $args ) )
+ $string = vsprintf( $string, $args );
+ }
+
+ $string = trim( $string );
+ $string = wp_kses( $string, array(
+ 'a' => array(
+ 'href' => true
+ ),
+ 'br' => true,
+ 'em' => true,
+ 'strong' => true,
+ ) );
+
+ $this->set_main_error_message( $string, $current_error );
+ $this->messages[] = $string;
+ }
}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php
index 4fbddaba..13534128 100644
--- a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php
@@ -49,7 +49,7 @@ class Jetpack_JSON_API_Plugins_Modify_Endpoint extends Jetpack_JSON_API_Plugins_
protected function autoupdate_on() {
$autoupdate_plugins = Jetpack_Options::get_option( 'autoupdate_plugins', array() );
- $autoupdate_plugins = array_unique( array_merge( $autoupdate_plugins, $this->plugins) );
+ $autoupdate_plugins = array_unique( array_merge( $autoupdate_plugins, $this->plugins ) );
Jetpack_Options::update_option( 'autoupdate_plugins', $autoupdate_plugins );
}
@@ -67,7 +67,7 @@ class Jetpack_JSON_API_Plugins_Modify_Endpoint extends Jetpack_JSON_API_Plugins_
continue;
}
- if( ! $this->network_wide && is_network_only_plugin( $plugin ) ) {
+ if ( ! $this->network_wide && is_network_only_plugin( $plugin ) && is_multisite() ) {
$this->log[ $plugin ]['error'] = __( 'Plugin can only be Network Activated', 'jetpack' );
$has_errors = true;
continue;
@@ -82,7 +82,7 @@ class Jetpack_JSON_API_Plugins_Modify_Endpoint extends Jetpack_JSON_API_Plugins_
}
$success = Jetpack::is_plugin_active( $plugin );
- if ( $success && $this->network_wide ) {
+ if ( $success && $this->network_wide ) {
$success &= is_plugin_active_for_network( $plugin );
}
@@ -100,7 +100,7 @@ class Jetpack_JSON_API_Plugins_Modify_Endpoint extends Jetpack_JSON_API_Plugins_
}
protected function deactivate() {
- foreach( $this->plugins as $plugin ) {
+ foreach ( $this->plugins as $plugin ) {
if ( ! Jetpack::is_plugin_active( $plugin ) ) {
$error = $this->log[ $plugin ]['error'] = __( 'The Plugin is already deactivated.', 'jetpack' );
continue;
@@ -109,7 +109,7 @@ class Jetpack_JSON_API_Plugins_Modify_Endpoint extends Jetpack_JSON_API_Plugins_
deactivate_plugins( $plugin, false, $this->network_wide );
$success = ! Jetpack::is_plugin_active( $plugin );
- if ( $success && $this->network_wide ) {
+ if ( $success && $this->network_wide ) {
$success &= ! is_plugin_active_for_network( $plugin );
}
@@ -166,7 +166,7 @@ class Jetpack_JSON_API_Plugins_Modify_Endpoint extends Jetpack_JSON_API_Plugins_
defined( 'DOING_CRON' ) or define( 'DOING_CRON', true );
$result = $upgrader->upgrade( $plugin );
- $this->log[ $plugin ][] = $upgrader->skin->get_upgrade_messages();
+ $this->log[ $plugin ][] = $upgrader->skin->get_upgrade_messages();
}
if ( ! $this->bulk && ! $result && $update_attempted ) {
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php
index 8f6b1dd6..f2a98fb9 100644
--- a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php
@@ -6,6 +6,7 @@ class Jetpack_JSON_API_Sync_Endpoint extends Jetpack_JSON_API_Endpoint {
protected function result() {
Jetpack::init();
+ /** This action is documented in class.jetpack.php */
do_action( 'jetpack_sync_all_registered_options' );
$result['scheduled'] = true;
return $result;
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php
index c479a4c4..4e6cf42e 100644
--- a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php
@@ -12,12 +12,16 @@ abstract class Jetpack_JSON_API_Themes_Endpoint extends Jetpack_JSON_API_Endpoin
protected $bulk = true;
protected $log;
+ protected $current_theme_id;
static $_response_format = array(
'id' => '(string) The theme\'s ID.',
'screenshot' => '(string) A theme screenshot URL',
'name' => '(string) The name of the theme.',
+ 'theme_uri' => '(string) The URI of the theme\'s webpage.',
'description' => '(string) A description of the theme.',
+ 'author' => '(string) The author of the theme.',
+ 'author_uri' => '(string) The website of the theme author.',
'tags' => '(array) Tags indicating styles and features of the theme.',
'log' => '(array) An array of log strings',
'autoupdate' => '(bool) Whether the theme is automatically updated',
@@ -92,7 +96,10 @@ abstract class Jetpack_JSON_API_Themes_Endpoint extends Jetpack_JSON_API_Endpoin
$fields = array(
'name' => 'Name',
+ 'theme_uri' => 'ThemeURI',
'description' => 'Description',
+ 'author' => 'Author',
+ 'author_uri' => 'AuthorURI',
'tags' => 'Tags',
'version' => 'Version'
);
@@ -100,7 +107,8 @@ abstract class Jetpack_JSON_API_Themes_Endpoint extends Jetpack_JSON_API_Endpoin
$id = $theme->get_stylesheet();
$formatted_theme = array(
'id' => $id,
- 'screenshot' => jetpack_photon_url( $theme->get_screenshot(), array(), 'network_path' )
+ 'screenshot' => jetpack_photon_url( $theme->get_screenshot(), array(), 'network_path' ),
+ 'active' => $id === $this->current_theme_id,
);
foreach( $fields as $key => $field ) {
@@ -152,6 +160,8 @@ abstract class Jetpack_JSON_API_Themes_Endpoint extends Jetpack_JSON_API_Endpoin
if ( isset( $args['limit'] ) )
$themes = array_slice( $themes, 0, (int) $args['limit'] );
+ $this->current_theme_id = wp_get_theme()->get_stylesheet();
+
return array_map( array( $this, 'format_theme' ), $themes );
}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php
new file mode 100644
index 00000000..d4a291de
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php
@@ -0,0 +1,39 @@
+<?php
+
+class WPCOM_JSON_API_Get_Option_Endpoint extends Jetpack_JSON_API_Endpoint {
+
+ protected $needed_capabilities = 'manage_options';
+
+ public $option_name;
+ public $site_option;
+
+ function result() {
+ if ( $this->site_option ) {
+ return array( 'option_value' => get_site_option( $this->option_name ) );
+ }
+ return array( 'option_value' => get_option( $this->option_name ) );
+ }
+
+ function validate_input( $object ) {
+ $query_args = $this->query_args();
+ $this->option_name = isset( $query_args['option_name'] ) ? $query_args['option_name'] : false;
+ if ( ! $this->option_name ) {
+ return new WP_Error( 'option_name_not_set', __( 'You must specify an option_name', 'jetpack' ) );
+ }
+ $this->site_option = isset( $query_args['site_option'] ) ? $query_args['site_option'] : false;
+ /**
+ * Filter the list of options that are manageable via the JSON API.
+ *
+ * @module json-api
+ *
+ * @since 3.8.2
+ *
+ * @param array The default list of site options.
+ * @param bool Is the option a site option.
+ */
+ if ( ! in_array( $this->option_name, apply_filters( 'jetpack_options_whitelist', array(), $this->site_option ) ) ) {
+ return new WP_Error( 'option_name_not_in_whitelist', __( 'You must specify a whitelisted option_name', 'jetpack' ) );
+ }
+ return true;
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-update-option-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-update-option-endpoint.php
new file mode 100644
index 00000000..20f8895c
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-update-option-endpoint.php
@@ -0,0 +1,31 @@
+<?php
+
+class WPCOM_JSON_API_Update_Option_Endpoint extends WPCOM_JSON_API_Get_Option_Endpoint {
+ public $option_value;
+
+ function result() {
+ if ( $this->site_option ) {
+ update_site_option( $this->option_name, $this->option_value );
+ } else {
+ update_option( $this->option_name, $this->option_value );
+ }
+ return parent::result();
+ }
+
+ function validate_input( $object ) {
+ $input = $this->input();
+ $query_args = $this->query_args();
+ if ( ! isset( $input['option_value'] ) || is_array( $input['option_value'] ) ) {
+ return new WP_Error( 'option_value_not_set', __( 'You must specify an option_value', 'jetpack' ) );
+ }
+ if ( $query_args['is_array'] ) {
+ // When converted back from JSON, the value is an object.
+ // Cast it to an array for options that expect arrays.
+ $this->option_value = (array) $input['option_value'];
+ } else {
+ $this->option_value = $input['option_value'];
+ }
+
+ return parent::validate_input( $object );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php b/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php
index b5f9c3e2..2424d075 100644
--- a/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php
+++ b/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php
@@ -578,3 +578,96 @@ new Jetpack_JSON_API_Sync_Endpoint( array(
),
'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync'
) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-log-endpoint.php' );
+
+new Jetpack_JSON_API_Jetpack_Log_Endpoint( array(
+ 'description' => 'Get the Jetpack log',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/jetpack-log',
+ 'stat' => 'log',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'event' => '(string) The event to filter by, by default all entries are returned',
+ 'num' => '(int) The number of entries to get, by default all entries are returned'
+ ),
+ 'response_format' => array(
+ 'log' => '(array) An array of jetpack log entries'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/jetpack-log'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-maybe-auto-update-endpoint.php' );
+
+new Jetpack_JSON_API_Maybe_Auto_Update_Endpoint( array(
+ 'description' => 'Maybe Auto Update Core, Plugins, Themes and Languages',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/maybe-auto-update',
+ 'stat' => 'maybe-auto-update',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'log' => '(array) Results of running the update job'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/maybe-auto-update'
+
+) );
+
+// Options
+require_once( $json_jetpack_endpoints_dir . 'class.wpcom-json-api-get-option-endpoint.php' );
+
+new WPCOM_JSON_API_Get_Option_Endpoint( array (
+ 'method' => 'GET',
+ 'description' => 'Fetches an option.',
+ 'group' => '__do_not_document',
+ 'stat' => 'option',
+ 'path' => '/sites/%s/option',
+ 'path_labels' => array(
+ '$site' => '(int|string) Site ID or domain',
+ ),
+ 'query_parameters' => array(
+ 'option_name' => '(string) The name of the option to fetch.',
+ 'site_option' => '(bool=false) True if the option is a site option.',
+ ),
+ 'response_format' => array(
+ 'option_value' => '(string|object) The value of the option.',
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/option?option_name=blogname',
+ 'example_request_data' => array(
+ 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
+ ),
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.wpcom-json-api-update-option-endpoint.php' );
+
+new WPCOM_JSON_API_Update_Option_Endpoint( array (
+ 'method' => 'POST',
+ 'description' => 'Updates an option.',
+ 'group' => '__do_not_document',
+ 'stat' => 'option:update',
+ 'path' => '/sites/%s/option',
+ 'path_labels' => array(
+ '$site' => '(int|string) Site ID or domain',
+ ),
+ 'query_parameters' => array(
+ 'option_name' => '(string) The name of the option to fetch.',
+ 'site_option' => '(bool=false) True if the option is a site option.',
+ 'is_array' => '(bool=false) True if the value should be converted to an array before saving.',
+ ),
+ 'request_format' => array(
+ 'option_value' => '(string|object) The new value of the option.',
+ ),
+ 'response_format' => array(
+ 'option_value' => '(string|object) The value of the updated option.',
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/option',
+ 'example_request_data' => array(
+ 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
+ 'body' => array(
+ 'option_value' => 'My new blog name'
+ ),
+ ),
+) );