diff options
author | Anthony G. Basile <blueness@gentoo.org> | 2017-06-15 08:53:57 -0400 |
---|---|---|
committer | Anthony G. Basile <blueness@gentoo.org> | 2017-06-15 08:53:57 -0400 |
commit | 48822ba710570832bbc0ffb9b6c3470e25e7bf29 (patch) | |
tree | 70e349b7e03191c456033287e53593056249c962 /plugins/jetpack/json-endpoints | |
parent | Update twentyfourteen 2.0 (diff) | |
download | blogs-gentoo-48822ba710570832bbc0ffb9b6c3470e25e7bf29.tar.gz blogs-gentoo-48822ba710570832bbc0ffb9b6c3470e25e7bf29.tar.bz2 blogs-gentoo-48822ba710570832bbc0ffb9b6c3470e25e7bf29.zip |
Update jetpack 5.0
Signed-off-by: Anthony G. Basile <blueness@gentoo.org>
Diffstat (limited to 'plugins/jetpack/json-endpoints')
34 files changed, 1342 insertions, 311 deletions
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 6f79222a..6f3152cd 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 @@ -10,15 +10,17 @@ class WPCOM_JSON_API_Get_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_End $args = $this->query_args(); + $site = $this->get_platform()->get_site( $blog_id ); + if ( false !== strpos( $path, '/posts/slug:' ) ) { - $post_id = $this->get_platform()->get_site( $blog_id )->get_post_id_by_name( $post_id ); + $post_id = $site->get_post_id_by_name( $post_id ); if ( is_wp_error( $post_id ) ) { return $post_id; } } if ( defined( 'IS_WPCOM' ) && IS_WPCOM && - ! in_array( get_post_type( $post_id ), array( false, 'post', 'page', 'revision' ) ) ) { + ! in_array( get_post_type( $post_id ), array( false, 'post', 'revision' ) ) ) { $this->load_theme_functions(); } @@ -28,7 +30,7 @@ class WPCOM_JSON_API_Get_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_End return $return; } - if ( ! $this->current_user_can_access_post_type( $return['type'], $args['context'] ) ) { + if ( ! $site->current_user_can_access_post_type( $return['type'], $args['context'] ) ) { return new WP_Error( 'unknown_post', 'Unknown post', 404 ); } 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 75ad1f3b..3ebd3bc1 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 @@ -25,6 +25,7 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { 'updates' => '(array) An array of available updates for plugins, themes, wordpress, and languages.', 'jetpack_modules' => '(array) A list of active Jetpack modules.', 'meta' => '(object) Meta data', + 'quota' => '(array) An array describing how much space a user has left for uploads', ); protected static $no_member_fields = array( @@ -86,10 +87,14 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { 'page_on_front', 'page_for_posts', 'headstart', + 'headstart_is_fresh', 'ak_vp_bundle_enabled', - 'verification_services_codes', Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION, Jetpack_SEO_Titles::TITLE_FORMATS_OPTION, + 'verification_services_codes', + 'podcasting_archive', + 'is_domain_only', + 'is_automated_transfer', ); protected static $jetpack_response_field_additions = array( @@ -103,7 +108,9 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { protected static $jetpack_response_option_additions = array( 'publicize_permanently_disabled', - 'ak_vp_bundle_enabled' + 'ak_vp_bundle_enabled', + 'is_automated_transfer', + 'frame_nonce' ); private $site; @@ -174,13 +181,33 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { array_intersect( $default_fields, $this->fields_to_include ) : $default_fields; - if ( ! is_user_member_of_blog( get_current_user_id(), get_current_blog_id() ) ) { + if ( ! $this->has_blog_access( $this->api->token_details, $blog_id ) ) { $response_keys = array_intersect( $response_keys, self::$no_member_fields ); } return $this->render_response_keys( $response_keys ); } + private function has_blog_access( $token_details, $blog_id ) { + if ( is_user_member_of_blog( get_current_user_id(), $blog_id ) ) { + return true; + } + + $token_details = (array) $token_details; + if ( ! isset( $token_details['access'], $token_details['auth'], $token_details['blog_id'] ) ) { + return false; + } + + if ( + 'jetpack' === $token_details['auth'] && + 'blog' === $token_details['access'] && + $blog_id === $token_details['blog_id'] + ) { + return true; + } + return false; + } + private function render_response_keys( &$response_keys ) { $response = array(); @@ -289,6 +316,9 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { case 'plan' : $response[ $key ] = $this->site->get_plan(); break; + case 'quota' : + $response[ $key ] = $this->site->get_quota(); + break; } do_action( 'post_render_site_response_key', $key ); @@ -304,7 +334,6 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { $custom_front_page = $site->is_custom_front_page(); - foreach ( $options_response_keys as $key ) { switch ( $key ) { case 'timezone' : @@ -429,6 +458,9 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { case 'headstart' : $options[ $key ] = $site->is_headstart(); break; + case 'headstart_is_fresh' : + $options[ $key ] = $site->is_headstart_fresh(); + break; case 'ak_vp_bundle_enabled' : $options[ $key ] = $site->get_ak_vp_bundle_enabled(); break; @@ -441,6 +473,15 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { case 'verification_services_codes' : $options[ $key ] = $site->get_verification_services_codes(); break; + case 'podcasting_archive': + $options[ $key ] = $site->get_podcasting_archive(); + break; + case 'is_domain_only': + $options[ $key ] = $site->is_domain_only(); + break; + case 'is_automated_transfer': + $options[ $key ] = $site->is_automated_transfer(); + break; } } @@ -469,6 +510,7 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { // apply any WPCOM-only response components to a Jetpack site response public function decorate_jetpack_response( &$response ) { $this->site = $this->get_platform()->get_site( $response->ID ); + switch_to_blog( $this->site->get_id() ); // ensure the response is marked as being from Jetpack $response->jetpack = true; @@ -479,8 +521,7 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { $response->{ $key } = $value; } - $token_details = (object) $this->api->token_details; - if ( is_user_member_of_blog( get_current_user_id(), get_current_blog_id() ) || 'blog' === $token_details->access ) { + if ( $this->has_blog_access( $this->api->token_details, $response->ID ) ) { $wpcom_member_response = $this->render_response_keys( self::$jetpack_response_field_member_additions ); foreach( $wpcom_member_response as $key => $value ) { @@ -508,6 +549,7 @@ class WPCOM_JSON_API_GET_Site_Endpoint extends WPCOM_JSON_API_Endpoint { } } + restore_current_blog(); return $response; // possibly no need since it's modified in place } } diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-v1-2-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-v1-2-endpoint.php index 898df417..e705e0eb 100644 --- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-v1-2-endpoint.php +++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-get-site-v1-2-endpoint.php @@ -1,4 +1,5 @@ <?php + class WPCOM_JSON_API_GET_Site_V1_2_Endpoint extends WPCOM_JSON_API_GET_Site_Endpoint { public static $site_format = array( @@ -24,6 +25,7 @@ class WPCOM_JSON_API_GET_Site_V1_2_Endpoint extends WPCOM_JSON_API_GET_Site_Endp 'updates' => '(array) An array of available updates for plugins, themes, wordpress, and languages.', 'jetpack_modules' => '(array) A list of active Jetpack modules.', 'meta' => '(object) Meta data', + 'quota' => '(array) An array describing how much space a user has left for uploads', ); 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 fc2e479a..fb910908 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 @@ -97,6 +97,7 @@ class WPCOM_JSON_API_List_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint { } $response = array(); + foreach ( $media->posts as $item ) { $response[] = $this->get_media_item_v1_1( $item->ID ); } diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-media-v1-2-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-media-v1-2-endpoint.php new file mode 100644 index 00000000..702129e6 --- /dev/null +++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-media-v1-2-endpoint.php @@ -0,0 +1,30 @@ +<?php + +jetpack_require_lib( 'class.media' ); + +class WPCOM_JSON_API_List_Media_v1_2_Endpoint extends WPCOM_JSON_API_List_Media_v1_1_Endpoint { + function callback( $path = '', $blog_id = 0 ) { + $response = parent::callback( $path, $blog_id ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $media_list = $response['media']; + + if ( count( $media_list ) < 1 ) { + return $response; + } + + foreach ( $media_list as $index => $media_item ) { + // expose `revision_history` object for each image + $media_item->revision_history = (object) array( + 'items' => (array) Media::get_revision_history( $media_item->ID ), + 'original' => (object) Media::get_original_media( $media_item->ID ) + ); + } + + return $response; + } +} + 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 1ab369f0..db82ae9b 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 @@ -172,6 +172,23 @@ class WPCOM_JSON_API_List_Posts_Endpoint extends WPCOM_JSON_API_Post_Endpoint { $query['tag'] = $args['tag']; } + if ( ! empty( $args['term'] ) ) { + $query['tax_query'] = array(); + foreach ( $args['term'] as $taxonomy => $slug ) { + $taxonomy_object = get_taxonomy( $taxonomy ); + if ( false === $taxonomy_object || ( ! $taxonomy_object->public && + ! current_user_can( $taxonomy_object->cap->assign_terms ) ) ) { + continue; + } + + $query['tax_query'][] = array( + 'taxonomy' => $taxonomy, + 'field' => 'slug', + 'terms' => explode( ',', $slug ) + ); + } + } + if ( isset( $args['page'] ) ) { if ( $args['page'] < 1 ) { $args['page'] = 1; 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 61953a1f..c50ac926 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 @@ -21,6 +21,7 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E $args = $this->query_args(); $is_eligible_for_page_handle = true; + $site = $this->get_platform()->get_site( $blog_id ); if ( $args['number'] < 1 ) { $args['number'] = 20; @@ -28,7 +29,7 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E return new WP_Error( 'invalid_number', 'The NUMBER parameter must be less than or equal to 100.', 400 ); } - if ( isset( $args['type'] ) && ! $this->is_post_type_allowed( $args['type'] ) ) { + if ( isset( $args['type'] ) && ! $site->is_post_type_allowed( $args['type'] ) ) { return new WP_Error( 'unknown_post_type', 'Unknown post type', 404 ); } @@ -37,7 +38,7 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E if ( version_compare( $this->api->version, '1.1', '<' ) ) { $args['type'] = array( 'post', 'page' ); } else { // 1.1+ - $args['type'] = $this->_get_whitelisted_post_types(); + $args['type'] = $site->get_whitelisted_post_types(); } } @@ -68,7 +69,7 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E } if ( isset( $args['type'] ) && - ! in_array( $args['type'], array( 'post', 'page', 'revision', 'any' ) ) && + ! in_array( $args['type'], array( 'post', 'revision', 'page', 'any' ) ) && defined( 'IS_WPCOM' ) && IS_WPCOM ) { $this->load_theme_functions(); } @@ -80,7 +81,7 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E if ( is_array( $args['type'] ) ) { $allowed_types = array(); foreach ( $args['type'] as $post_type ) { - if ( $this->current_user_can_access_post_type( $post_type, $args['context'] ) ) { + if ( $site->current_user_can_access_post_type( $post_type, $args['context'] ) ) { $allowed_types[] = $post_type; } } @@ -91,7 +92,7 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E $args['type'] = $allowed_types; } else { - if ( ! $this->current_user_can_access_post_type( $args['type'], $args['context'] ) ) { + if ( ! $site->current_user_can_access_post_type( $args['type'], $args['context'] ) ) { return array( 'found' => 0, 'posts' => array() ); } } @@ -183,6 +184,23 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E $query['tag'] = $args['tag']; } + if ( ! empty( $args['term'] ) ) { + $query['tax_query'] = array(); + foreach ( $args['term'] as $taxonomy => $slug ) { + $taxonomy_object = get_taxonomy( $taxonomy ); + if ( false === $taxonomy_object || ( ! $taxonomy_object->public && + ! current_user_can( $taxonomy_object->cap->assign_terms ) ) ) { + continue; + } + + $query['tax_query'][] = array( + 'taxonomy' => $taxonomy, + 'field' => 'slug', + 'terms' => explode( ',', $slug ) + ); + } + } + if ( isset( $args['page'] ) ) { if ( $args['page'] < 1 ) { $args['page'] = 1; @@ -311,6 +329,13 @@ class WPCOM_JSON_API_List_Posts_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_E $return[$key]->next_page = $this->build_page_handle( $last_post, $query ); } } + + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + if ( !isset( $return[$key] ) ) + $return[$key] = new stdClass; + $return[$key]->wpcom = true; + } + break; } } diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-v1-2-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-v1-2-endpoint.php new file mode 100644 index 00000000..664fd45f --- /dev/null +++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-list-posts-v1-2-endpoint.php @@ -0,0 +1,382 @@ +<?php +/* + * WARNING: This file is distributed verbatim in Jetpack. + * There should be nothing WordPress.com specific in this file. + * + * @hide-in-jetpack + */ + +class WPCOM_JSON_API_List_Posts_v1_2_Endpoint extends WPCOM_JSON_API_List_Posts_v1_1_Endpoint { + // /sites/%s/posts/ -> $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; + } + + $args = $this->query_args(); + $is_eligible_for_page_handle = true; + $site = $this->get_platform()->get_site( $blog_id ); + + if ( $args['number'] < 1 ) { + $args['number'] = 20; + } elseif ( 100 < $args['number'] ) { + return new WP_Error( 'invalid_number', 'The NUMBER parameter must be less than or equal to 100.', 400 ); + } + + if ( isset( $args['type'] ) ) { + // load all types on WPCOM, unless only built-in ones are requested + if ( defined( 'IS_WPCOM' ) && IS_WPCOM && ! in_array( $args['type'], array( 'post', 'revision', 'page' ) ) ) { + $this->load_theme_functions(); + } + + if ( ! $site->is_post_type_allowed( $args['type'] ) ) { + return new WP_Error( 'unknown_post_type', 'Unknown post type', 404 ); + } + + // Normalize post_type + if ( 'any' == $args['type'] ) { + $whitelisted_post_types = $site->get_whitelisted_post_types(); + + if ( isset( $args['exclude_private_types'] ) && $args['exclude_private_types'] == true ) { + $public_post_types = get_post_types( array( 'public' => true ) ); + $args['type'] = array_intersect( $public_post_types, $whitelisted_post_types ); + } else { + $args['type'] = $whitelisted_post_types; + } + } + } else { + // let's be explicit about defaulting to 'post' + $args['type'] = 'post'; + } + + // make sure the user can read or edit the requested post type(s) + if ( is_array( $args['type'] ) ) { + $allowed_types = array(); + foreach ( $args['type'] as $post_type ) { + if ( $site->current_user_can_access_post_type( $post_type, $args['context'] ) ) { + $allowed_types[] = $post_type; + } + } + + if ( empty( $allowed_types ) ) { + return array( 'found' => 0, 'posts' => array() ); + } + $args['type'] = $allowed_types; + } + else { + if ( ! $site->current_user_can_access_post_type( $args['type'], $args['context'] ) ) { + return array( 'found' => 0, 'posts' => array() ); + } + } + + // determine statuses + $status = ( ! empty( $args['status'] ) ) ? explode( ',', $args['status'] ) : array( 'publish' ); + if ( is_user_logged_in() ) { + $statuses_whitelist = array( + 'publish', + 'pending', + 'draft', + 'future', + 'private', + 'trash', + 'any', + ); + $status = array_intersect( $status, $statuses_whitelist ); + } else { + // logged-out users can see only published posts + $statuses_whitelist = array( 'publish', 'any' ); + $status = array_intersect( $status, $statuses_whitelist ); + + if ( empty( $status ) ) { + // requested only protected statuses? nothing for you here + return array( 'found' => 0, 'posts' => array() ); + } + // clear it (AKA published only) because "any" includes protected + $status = array(); + } + + $query = array( + 'posts_per_page' => $args['number'], + 'order' => $args['order'], + 'orderby' => $args['order_by'], + 'post_type' => $args['type'], + 'post_status' => $status, + 'post_parent' => isset( $args['parent_id'] ) ? $args['parent_id'] : null, + 'author' => isset( $args['author'] ) && 0 < $args['author'] ? $args['author'] : null, + 's' => isset( $args['search'] ) ? $args['search'] : null, + 'fields' => 'ids', + ); + + if ( ! is_user_logged_in () ) { + $query['has_password'] = false; + } + + if ( isset( $args['meta_key'] ) ) { + $show = false; + if ( WPCOM_JSON_API_Metadata::is_public( $args['meta_key'] ) ) + $show = true; + if ( current_user_can( 'edit_post_meta', $query['post_type'], $args['meta_key'] ) ) + $show = true; + + if ( is_protected_meta( $args['meta_key'], 'post' ) && ! $show ) + return new WP_Error( 'invalid_meta_key', 'Invalid meta key', 404 ); + + $meta = array( 'key' => $args['meta_key'] ); + if ( isset( $args['meta_value'] ) ) + $meta['value'] = $args['meta_value']; + + $query['meta_query'] = array( $meta ); + } + + if ( $args['sticky'] === 'include' ) { + $query['ignore_sticky_posts'] = 1; + } else if ( $args['sticky'] === 'exclude' ) { + $sticky = get_option( 'sticky_posts' ); + if ( is_array( $sticky ) ) { + $query['post__not_in'] = $sticky; + } + } else if ( $args['sticky'] === 'require' ) { + $sticky = get_option( 'sticky_posts' ); + if ( is_array( $sticky ) && ! empty( $sticky ) ) { + $query['post__in'] = $sticky; + } else { + // no sticky posts exist + return array( 'found' => 0, 'posts' => array() ); + } + } + + if ( isset( $args['exclude'] ) ) { + $excluded_ids = (array) $args['exclude']; + $query['post__not_in'] = isset( $query['post__not_in'] ) ? array_merge( $query['post__not_in'], $excluded_ids ) : $excluded_ids; + } + + if ( isset( $args['exclude_tree'] ) && is_post_type_hierarchical( $args['type'] ) ) { + // get_page_children is a misnomer; it supports all hierarchical post types + $page_args = array( + 'child_of' => $args['exclude_tree'], + 'post_type' => $args['type'], + // since we're looking for things to exclude, be aggressive + 'post_status' => 'publish,draft,pending,private,future,trash', + ); + $post_descendants = get_pages( $page_args ); + + $exclude_tree = array( $args['exclude_tree'] ); + foreach ( $post_descendants as $child ) { + $exclude_tree[] = $child->ID; + } + + $query['post__not_in'] = isset( $query['post__not_in'] ) ? array_merge( $query['post__not_in'], $exclude_tree ) : $exclude_tree; + } + + if ( isset( $args['category'] ) ) { + $category = get_term_by( 'slug', $args['category'], 'category' ); + if ( $category === false) { + $query['category_name'] = $args['category']; + } else { + $query['cat'] = $category->term_id; + } + } + + if ( isset( $args['tag'] ) ) { + $query['tag'] = $args['tag']; + } + + if ( ! empty( $args['term'] ) ) { + $query['tax_query'] = array(); + foreach ( $args['term'] as $taxonomy => $slug ) { + $taxonomy_object = get_taxonomy( $taxonomy ); + if ( false === $taxonomy_object || ( ! $taxonomy_object->public && + ! current_user_can( $taxonomy_object->cap->assign_terms ) ) ) { + continue; + } + + $query['tax_query'][] = array( + 'taxonomy' => $taxonomy, + 'field' => 'slug', + 'terms' => explode( ',', $slug ) + ); + } + } + + if ( isset( $args['page'] ) ) { + if ( $args['page'] < 1 ) { + $args['page'] = 1; + } + + $query['paged'] = $args['page']; + if ( $query['paged'] !== 1 ) { + $is_eligible_for_page_handle = false; + } + } else { + if ( $args['offset'] < 0 ) { + $args['offset'] = 0; + } + + $query['offset'] = $args['offset']; + if ( $query['offset'] !== 0 ) { + $is_eligible_for_page_handle = false; + } + } + + if ( isset( $args['before'] ) ) { + $this->date_range['before'] = $args['before']; + } + if ( isset( $args['after'] ) ) { + $this->date_range['after'] = $args['after']; + } + + if ( isset( $args['modified_before_gmt'] ) ) { + $this->modified_range['before'] = $args['modified_before_gmt']; + } + if ( isset( $args['modified_after_gmt'] ) ) { + $this->modified_range['after'] = $args['modified_after_gmt']; + } + + if ( $this->date_range ) { + add_filter( 'posts_where', array( $this, 'handle_date_range' ) ); + } + + if ( $this->modified_range ) { + add_filter( 'posts_where', array( $this, 'handle_modified_range' ) ); + } + + if ( isset( $args['page_handle'] ) ) { + $page_handle = wp_parse_args( $args['page_handle'] ); + if ( isset( $page_handle['value'] ) && isset( $page_handle['id'] ) ) { + // we have a valid looking page handle + $this->page_handle = $page_handle; + add_filter( 'posts_where', array( $this, 'handle_where_for_page_handle' ) ); + } + } + + /** + * 'column' necessary for the me/posts endpoint (which extends sites/$site/posts). + * Would need to be added to the sites/$site/posts definition if we ever want to + * use it there. + */ + $column_whitelist = array( 'post_modified_gmt' ); + if ( isset( $args['column'] ) && in_array( $args['column'], $column_whitelist ) ) { + $query['column'] = $args['column']; + } + + $this->performed_query = $query; + add_filter( 'posts_orderby', array( $this, 'handle_orderby_for_page_handle' ) ); + + $wp_query = new WP_Query( $query ); + + remove_filter( 'posts_orderby', array( $this, 'handle_orderby_for_page_handle' ) ); + + if ( $this->date_range ) { + remove_filter( 'posts_where', array( $this, 'handle_date_range' ) ); + $this->date_range = array(); + } + + if ( $this->modified_range ) { + remove_filter( 'posts_where', array( $this, 'handle_modified_range' ) ); + $this->modified_range = array(); + } + + if ( $this->page_handle ) { + remove_filter( 'posts_where', array( $this, 'handle_where_for_page_handle' ) ); + + } + + $return = array(); + $excluded_count = 0; + foreach ( array_keys( $this->response_format ) as $key ) { + switch ( $key ) { + case 'found' : + $return[$key] = (int) $wp_query->found_posts; + break; + case 'posts' : + $posts = array(); + foreach ( $wp_query->posts as $post_ID ) { + $the_post = $this->get_post_by( 'ID', $post_ID, $args['context'] ); + if ( $the_post && ! is_wp_error( $the_post ) ) { + $posts[] = $the_post; + } else { + $excluded_count++; + } + } + + 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; + + case 'meta' : + if ( ! is_array( $args['type'] ) ) { + $return[$key] = (object) array( + 'links' => (object) array( + 'counts' => (string) $this->links->get_site_link( $blog_id, 'post-counts/' . $args['type'] ), + ) + ); + } + + 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 ); + } + } + + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + if ( !isset( $return[$key] ) ) + $return[$key] = new stdClass; + $return[$key]->wpcom = true; + } + + break; + } + } + + $return['found'] -= $excluded_count; + + return $return; + } + + function build_page_handle( $post, $query ) { + $column = $query['orderby']; + if ( ! $column ) { + $column = 'date'; + } + return build_query( array( 'value' => urlencode($post[$column]), 'id' => $post['ID'] ) ); + } + + function _build_date_range_query( $column, $range, $where ) { + global $wpdb; + + switch ( count( $range ) ) { + case 2 : + $where .= $wpdb->prepare( + " AND `$wpdb->posts`.$column >= CAST( %s AS DATETIME ) AND `$wpdb->posts`.$column < CAST( %s AS DATETIME ) ", + $range['after'], + $range['before'] + ); + break; + case 1 : + if ( isset( $range['before'] ) ) { + $where .= $wpdb->prepare( + " AND `$wpdb->posts`.$column < CAST( %s AS DATETIME ) ", + $range['before'] + ); + } else { + $where .= $wpdb->prepare( + " AND `$wpdb->posts`.$column > CAST( %s AS DATETIME ) ", + $range['after'] + ); + } + break; + } + + return $where; + } +} 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 index 82464c3f..ec142eb8 100644 --- 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 @@ -45,14 +45,13 @@ class WPCOM_JSON_API_List_Roles_Endpoint extends WPCOM_JSON_API_Endpoint { $roles = array(); - global $wp_roles; - $wp_roles->reinit(); + $wp_roles= new WP_Roles(); $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]; + $role_details->display_name = translate_user_role( $role_names[$role_key] ); $roles[] = $role_details; } @@ -61,4 +60,4 @@ class WPCOM_JSON_API_List_Roles_Endpoint extends WPCOM_JSON_API_Endpoint { return array( 'roles' => $roles ); } -}
\ No newline at end of file +} 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 95f59288..14472ab9 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 @@ -65,35 +65,6 @@ abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint { parent::__construct( $args ); } - function is_metadata_public( $key ) { - if ( empty( $key ) ) - return false; - - // Default whitelisted meta keys. - $whitelisted_meta = array( '_thumbnail_id' ); - - /** - * 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; - - if ( 0 === strpos( $key, 'geo_' ) ) - return true; - - if ( 0 === strpos( $key, '_wpas_' ) ) - return true; - - return false; - } - function the_password_form() { return __( 'This post is password protected.', 'jetpack' ); } @@ -449,7 +420,7 @@ abstract class WPCOM_JSON_API_Post_Endpoint extends WPCOM_JSON_API_Endpoint { $show = true; // Only business plan subscribers can view custom meta description. - if ( Jetpack_SEO_Posts::DESCRIPTION_META_KEY == $meta->key && ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) { + if ( Jetpack_SEO_Posts::DESCRIPTION_META_KEY === $meta['meta_key'] && ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) { $show = false; } 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 93dade64..8dd17601 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 @@ -112,7 +112,18 @@ abstract class WPCOM_JSON_API_Post_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint setup_postdata( $post ); } - $response = $this->render_response_keys( $post, $context, array_keys( $this->post_object_format ) ); + $keys_to_render = array_keys( $this->post_object_format ); + if ( isset( $this->api->query[ 'fields' ] ) ) { + $limit_to_fields = array_map( 'trim', explode( ',', $this->api->query['fields'] ) ); + $keys_to_render = array_intersect( $keys_to_render, $limit_to_fields ); + } + + // always include 'type' because processors require it to validate access + if ( ! in_array( 'type', $keys_to_render ) ) { + array_push( $keys_to_render, 'type' ); + } + + $response = $this->render_response_keys( $post, $context, $keys_to_render ); unset( $GLOBALS['post'] ); 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 deleted file mode 100644 index bccfa84d..00000000 --- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-publicize-endpoint.php +++ /dev/null @@ -1,181 +0,0 @@ -<?php - -class WPCOM_JSON_API_Get_Connections_Endpoint extends WPCOM_JSON_API_Endpoint { - // /sites/%s/connections - function callback( $path = '', $blog_id = 0 ) { - // Verify required Publicize Jetpack module is active - if ( ! class_exists( 'Publicize' ) || ( method_exists( 'Jetpack', 'is_module_active' ) && ! Jetpack::is_module_active( 'publicize' ) ) ) { - return new WP_Error( 'missing_jetpack_module', 'The Publicize module must be activated in order to use this endpoint.', 400 ); - } - - // Authenticate user - $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; - } - - $current_user = wp_get_current_user(); - if ( ! $current_user->ID ) { - return new WP_Error( 'authorization_required', 'An active access token must be used to query information about the current user.', 403 ); - } - - // Parse query arguments to determine if filtering is requested - $args = $this->query_args(); - $service_filter = false; - if ( ! empty( $args['service'] ) ) { - $service_filter = $args['service']; - } - - // Iterate over connected services - $publicize = new Publicize(); - $connected_services = $publicize->get_services( 'connected' ); - $output = array(); - foreach( $connected_services as $service => $connections ) { - if ( false != $service_filter && $service_filter != $service ) { - continue; - } - - foreach ( $connections as $connection_id => $connection ) { - $output[] = WPCOM_JSON_API_Get_Connection_Endpoint::get_connection( $service, $connection ); - } - } - - return array( 'connections' => $output ); - } -} - -class WPCOM_JSON_API_Get_Connection_Endpoint extends WPCOM_JSON_API_Endpoint { - function get_connection_by_id( $connection_id ) { - $publicize = new Publicize(); - - $connected_services = $publicize->get_services( 'connected' ); - foreach ( $connected_services as $service => $connections ) { - foreach ( $connections as $c => $connection ) { - if ( $connection_id == $publicize->get_connection_id( $connection ) ) { - return WPCOM_JSON_API_Get_Connection_Endpoint::get_connection( $service, $connections[ $c ] ); - } - } - } - - return false; - } - - function get_connection( $service, $connection ) { - $publicize = new Publicize(); - - $connection_id = $publicize->get_connection_id( $connection ); - if ( method_exists( $connection, 'get_meta' ) ) { - $connection_meta = $connection->get_meta(); - $connection_data = (array) $connection->get_meta( 'connection_data' ); - } else { - $connection_meta = $connection; - $connection_data = $connection['connection_data']; - } - - return array( - 'ID' => (int) $connection_id, - 'token_ID' => (int) $connection_data['token_id'], - 'conn_ID' => (int) $connection_id, - 'site_ID' => (int) $connection_data['blog_id'], - 'user_ID' => (int) $connection_data['user_id'], - 'shared' => ( 0 == (int) $connection_data['user_id'] ) ? true : false, - 'service' => $service, - 'label' => $publicize->get_service_label( $service ), - 'issued' => $connection_meta['issued'], - 'expires' => $connection_meta['expires'], - 'external_ID' => $connection_meta['external_id'], - 'external_name' => $connection_meta['external_name'], - 'external_display' => $publicize->get_display_name( $service, $connection ), - 'URL' => $publicize->get_profile_link( $service, $connection ), - 'status' => ( method_exists( $connection, 'is_expired' ) && $connection->is_expired( HOUR_IN_SECONDS ) ) ? 'broken' : 'ok', - 'refresh_url' => $publicize->refresh_url( $service ), - 'meta' => maybe_unserialize( $connection_data['meta'] ), - ); - } - - // /sites/%s/connections/$connection_id - function callback( $path = '', $blog_id = 0, $connection_id = 0 ) { - // Verify required Publicize Jetpack module is active - if ( ! class_exists( 'Publicize' ) || ( method_exists( 'Jetpack', 'is_module_active' ) && ! Jetpack::is_module_active( 'publicize' ) ) ) { - return new WP_Error( 'missing_jetpack_module', 'The Publicize module must be activated in order to use this endpoint.', 400 ); - } - - $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; - } - - $current_user = wp_get_current_user(); - if ( ! $current_user->ID ) { - return new WP_Error( 'authorization_required', 'An active access token must be used to query information about the current user.', 403 ); - } - - // Attempt to find connection - $connection = WPCOM_JSON_API_Get_Connection_Endpoint::get_connection_by_id( $connection_id ); - - // 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 ); - } - - if ( empty( $connection ) ) { - return new WP_Error( 'unknown_connection', 'Connection not found.', 404 ); - } - - return $connection; - } -} - -class WPCOM_JSON_API_Delete_Connection_Endpoint extends WPCOM_JSON_API_Endpoint { - // /sites/%s/connections/$connection_id/delete - function callback( $path = '', $blog_id = 0 , $connection_id = 0 ) { - // Verify required Publicize Jetpack module is active - if ( ! class_exists( 'Publicize' ) || ( method_exists( 'Jetpack', 'is_module_active' ) && ! Jetpack::is_module_active( 'publicize' ) ) ) { - return new WP_Error( 'missing_jetpack_module', 'The Publicize module must be activated in order to use this endpoint.', 400 ); - } - - $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; - } - - $current_user = wp_get_current_user(); - if ( ! $current_user->ID ) { - return new WP_Error( 'authorization_required', 'An active access token must be used to query information about the current user.', 403 ); - } - - // Attempt to find connection - $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 ); - } - - // 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 ); - } - - // 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 ); - } - - return array( - 'ID' => (int) $connection_id, - 'deleted' => $is_deleted - ); - } -} 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 58abec3d..5aebe8d3 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 @@ -8,6 +8,7 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { 'description' => '(string) Tagline or description of site', 'URL' => '(string) Full URL to the site', 'lang' => '(string) Primary language code of the site', + 'locale_variant' => '(string) Locale variant code for the site, if set', 'settings' => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.', ); @@ -137,6 +138,14 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { case 'URL' : $response[$key] = (string) home_url(); break; + case 'locale_variant': + if ( function_exists( 'wpcom_l10n_get_blog_locale_variant' ) ) { + $blog_locale_variant = wpcom_l10n_get_blog_locale_variant(); + if ( $blog_locale_variant ) { + $response[$key] = $blog_locale_variant; + } + } + break; case 'settings': $jetpack_relatedposts_options = Jetpack_Options::get_option( 'relatedposts' ); @@ -198,8 +207,12 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { 'comment_max_links' => (int) get_option( 'comment_max_links' ), 'moderation_keys' => get_option( 'moderation_keys' ), 'blacklist_keys' => get_option( 'blacklist_keys' ), - 'lang_id' => get_option( 'lang_id' ), - 'wga' => $this->get_google_analytics(), + 'lang_id' => defined( 'IS_WPCOM' ) && IS_WPCOM + ? get_lang_id_by_code( wpcom_l10n_get_blog_locale_variant( $blog_id, true ) ) + : get_option( 'lang_id' ), + 'wga' => defined( 'IS_WPCOM' ) && IS_WPCOM + ? get_option( 'wga' ) + : $this->get_google_analytics(), 'disabled_likes' => (bool) get_option( 'disabled_likes' ), 'disabled_reblogs' => (bool) get_option( 'disabled_reblogs' ), 'jetpack_comment_likes_enabled' => (bool) get_option( 'jetpack_comment_likes_enabled', false ), @@ -216,12 +229,21 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { 'jetpack_testimonial_posts_per_page' => (int) get_option( 'jetpack_testimonial_posts_per_page', '10' ), 'jetpack_portfolio' => (bool) get_option( 'jetpack_portfolio', '0' ), 'jetpack_portfolio_posts_per_page' => (int) get_option( 'jetpack_portfolio_posts_per_page', '10' ), + 'markdown_supported' => true, 'site_icon' => $this->get_cast_option_value_or_null( 'site_icon', 'intval' ), Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION => get_option( Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION, '' ), Jetpack_SEO_Titles::TITLE_FORMATS_OPTION => get_option( Jetpack_SEO_Titles::TITLE_FORMATS_OPTION, array() ), + 'amp_is_supported' => (bool) function_exists( 'wpcom_is_amp_supported' ) && wpcom_is_amp_supported( $blog_id ), + 'amp_is_enabled' => (bool) function_exists( 'wpcom_is_amp_enabled' ) && wpcom_is_amp_enabled( $blog_id ), 'api_cache' => $api_cache, + 'posts_per_page' => (int) get_option( 'posts_per_page' ), ); + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + $response[ $key ]['wpcom_publish_posts_with_markdown'] = (bool) WPCom_Markdown::is_posting_enabled(); + $response[ $key ]['wpcom_publish_comments_with_markdown'] = (bool) WPCom_Markdown::is_commenting_enabled(); + } + //allow future versions of this endpoint to support additional settings keys /** * Filter the current site setting in the returned response. @@ -280,7 +302,6 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { * @return (array) */ public function update_settings() { - // $this->input() retrieves posted arguments whitelisted and casted to the $request_format // specs that get passed in when this class is instantiated /** @@ -294,6 +315,8 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { */ $input = apply_filters( 'rest_api_update_site_settings', $this->input() ); + $blog_id = get_current_blog_id(); + $jetpack_relatedposts_options = array(); $sharing_options = array(); $updated = array(); @@ -384,6 +407,7 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { $business_plugins->activate_plugin( 'wp-google-analytics' ); } break; + case 'jetpack_testimonial': case 'jetpack_portfolio': case 'jetpack_comment_likes_enabled': @@ -487,7 +511,15 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { // settings are stored as deletable numeric (all empty // values as delete intent), validated as media image if ( empty( $value ) || WPCOM_JSON_API::is_falsy( $value ) ) { - if ( delete_option( $key ) ) { + /** + * Fallback mechanism to clear a third party site icon setting. Can be used + * to unset the option when an API request instructs the site to remove the site icon. + * + * @module json-api + * + * @since 4.10 + */ + if ( delete_option( $key ) || apply_filters( 'rest_api_site_icon_cleared', false ) ) { $updated[ $key ] = null; } } else if ( is_numeric( $value ) ) { @@ -538,6 +570,23 @@ class WPCOM_JSON_API_Site_Settings_Endpoint extends WPCOM_JSON_API_Endpoint { } break; + case 'wpcom_publish_posts_with_markdown': + case 'wpcom_publish_comments_with_markdown': + $coerce_value = (bool) $value; + if ( update_option( $key, $coerce_value ) ) { + $updated[ $key ] = $coerce_value; + } + break; + + case 'amp_is_enabled': + if ( function_exists( 'wpcom_update_amp_enabled' ) ) { + $saved = wpcom_update_amp_enabled( $blog_id, $value ); + if ( $saved ) { + $updated[ $key ] = (bool) $value; + } + } + break; + default: //allow future versions of this endpoint to support additional settings keys if ( has_filter( 'site_settings_endpoint_update_' . $key ) ) { diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-v1-2-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-v1-2-endpoint.php index d682094d..872512b6 100644 --- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-v1-2-endpoint.php +++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-site-settings-v1-2-endpoint.php @@ -3,12 +3,13 @@ class WPCOM_JSON_API_Site_Settings_V1_2_Endpoint extends WPCOM_JSON_API_Site_Settings_Endpoint { public static $site_format = array( - 'ID' => '(int) Site ID', - 'name' => '(string) Title of site', - 'description' => '(string) Tagline or description of site', - 'URL' => '(string) Full URL to the site', - 'locale' => '(string) Locale code of the site', - 'settings' => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.', + 'ID' => '(int) Site ID', + 'name' => '(string) Title of site', + 'description' => '(string) Tagline or description of site', + 'URL' => '(string) Full URL to the site', + 'locale' => '(string) Locale code of the site', + 'locale_variant' => '(string) Locale variant code for the site, if set', + 'settings' => '(array) An array of options/settings for the blog. Only viewable by users with post editing rights to the site.', ); 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 index 7a61ce12..57de701c 100644 --- 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 @@ -1,5 +1,4 @@ <?php - class WPCOM_JSON_API_Site_User_Endpoint extends WPCOM_JSON_API_Endpoint { public static $user_format = array( @@ -14,7 +13,7 @@ class WPCOM_JSON_API_Site_User_Endpoint extends WPCOM_JSON_API_Endpoint { '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', + 'roles' => '(array|string) The role or roles of the user', ); // /sites/%s/users/%d -> $blog_id, $user_id @@ -55,7 +54,7 @@ class WPCOM_JSON_API_Site_User_Endpoint extends WPCOM_JSON_API_Endpoint { $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(); + $the_user->roles = ! is_wp_error( $userdata ) ? array_values( $userdata->roles ) : array(); } return $the_user; @@ -97,13 +96,23 @@ class WPCOM_JSON_API_Site_User_Endpoint extends WPCOM_JSON_API_Endpoint { } } } + if ( isset( $input[ 'roles' ] ) ) { + // For now, we only use the first role in the array. if ( is_array( $input['roles'] ) ) { $user['role'] = $input['roles'][0]; - } else { + } else if ( is_string( $input['roles'] ) ) { $user['role'] = $input['roles']; + } else { + return new WP_Error( 'invalid_input', __( 'The roles property must be a string or an array.', 'jetpack' ), 400 ); + } + + $editable_roles = array_keys( get_editable_roles() ); + if ( ! in_array( $user['role'], $editable_roles ) ) { + return new WP_Error( 'invalid_input', sprintf( __( '%s is not a valid role.', 'jetpack' ), $editable_roles ), 400 ); } } + $result = wp_update_user( $user ); if ( is_wp_error( $result ) ) { return $result; 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 cd912132..79632dd3 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 @@ -173,10 +173,6 @@ class WPCOM_JSON_API_Update_Comment_Endpoint extends WPCOM_JSON_API_Comment_Endp } if ( isset( $update['comment_status'] ) ) { - if ( count( $update ) === 1 ) { - // We are only here to update the comment status so let's respond ASAP - add_action( 'wp_set_comment_status', array( $this, 'output_comment' ), 0, 1 ); - } switch ( $update['comment_status'] ) { case 'approved' : if ( 'approve' !== $comment_status ) { @@ -263,10 +259,4 @@ class WPCOM_JSON_API_Update_Comment_Endpoint extends WPCOM_JSON_API_Comment_Endp return $this->get_comment( $comment->comment_ID, $args['context'] ); } - - function output_comment( $comment_id ) { - $args = $this->query_args(); - $output = $this->get_comment( $comment_id, $args['context'] ); - $this->api->output_early( 200, $output ); - } } 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 849fed88..4231f296 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 @@ -311,7 +311,16 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint { $insert['edit_date'] = true; } - $post_id = wp_update_post( (object) $insert ); + // this two-step process ensures any changes submitted along with status=trash get saved before trashing + if ( isset( $input['status'] ) && 'trash' === $input['status'] ) { + // if we insert it with status='trash', it will get double-trashed, so insert it as a draft first + unset( $insert['status'] ); + $post_id = wp_update_post( (object) $insert ); + // now call wp_trash_post so post_meta gets set and any filters get called + wp_trash_post( $post_id ); + } else { + $post_id = wp_update_post( (object) $insert ); + } } @@ -537,6 +546,10 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint { if ( ! empty( $meta->id ) ) { $meta->id = absint( $meta->id ); $existing_meta_item = get_metadata_by_mid( 'post', $meta->id ); + if ( $post_id !== (int) $existing_meta_item->post_id ) { + // Only allow updates for metadata on this post + continue; + } } $unslashed_meta_key = wp_unslash( $meta->key ); // should match what the final key will be @@ -648,7 +661,11 @@ class WPCOM_JSON_API_Update_Post_Endpoint extends WPCOM_JSON_API_Post_Endpoint { /** 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 ); + // we need to call wp_trash_post so that untrash will work correctly for all post types + if ( 'trash' === $post->post_status ) + wp_delete_post( $post->ID ); + else + wp_trash_post( $post->ID ); $status = get_post_status( $post->ID ); if ( false === $status ) { 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 0f697f0b..e74ec352 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 @@ -29,6 +29,8 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_ // /sites/%s/posts/new -> $blog_id // /sites/%s/posts/%d -> $blog_id, $post_id function write_post( $path, $blog_id, $post_id ) { + global $wpdb; + $new = $this->api->ends_with( $path, '/new' ); $args = $this->query_args(); @@ -139,9 +141,9 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_ } } - if ( function_exists( 'wpcom_switch_to_locale' ) ) { + if ( function_exists( 'wpcom_switch_to_blog_locale' ) ) { // fixes calypso-pre-oss #12476: respect blog locale when creating the post slug - wpcom_switch_to_locale( get_blog_lang_code( $blog_id ) ); + wpcom_switch_to_blog_locale( $blog_id ); } // If date was set, $this->input will set date_gmt, date still needs to be adjusted for the blog's offset @@ -330,8 +332,16 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_ $has_media = ! empty( $input['media'] ) ? count( $input['media'] ) : false; $has_media_by_url = ! empty( $input['media_urls'] ) ? count( $input['media_urls'] ) : false; - if ( $new ) { + $media_id_string = ''; + if ( $has_media || $has_media_by_url ) { + $media_files = ! empty( $input['media'] ) ? $input['media'] : array(); + $media_urls = ! empty( $input['media_urls'] ) ? $input['media_urls'] : array(); + $media_attrs = ! empty( $input['media_attrs'] ) ? $input['media_attrs'] : array(); + $media_results = $this->handle_media_creation_v1_1( $media_files, $media_urls, $media_attrs ); + $media_id_string = join( ',', array_filter( array_map( 'absint', $media_results['media_ids'] ) ) ); + } + if ( $new ) { if ( isset( $input['content'] ) && ! has_shortcode( $input['content'], 'gallery' ) && ( $has_media || $has_media_by_url ) ) { switch ( ( $has_media + $has_media_by_url ) ) { case 0 : @@ -339,11 +349,17 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_ break; case 1 : // 1 image - make it big - $insert['post_content'] = $input['content'] = "[gallery size=full columns=1]\n\n" . $input['content']; + $insert['post_content'] = $input['content'] = sprintf( + "[gallery size=full ids='%s' columns=1]\n\n", + $media_id_string + ) . $input['content']; break; default : // Several images - 3 column gallery - $insert['post_content'] = $input['content'] = "[gallery]\n\n" . $input['content']; + $insert['post_content'] = $input['content'] = sprintf( + "[gallery ids='%s']\n\n", + $media_id_string + ) . $input['content']; break; } } @@ -359,7 +375,16 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_ $insert['edit_date'] = true; } - $post_id = wp_update_post( (object) $insert ); + // this two-step process ensures any changes submitted along with status=trash get saved before trashing + if ( isset( $input['status'] ) && 'trash' === $input['status'] ) { + // if we insert it with status='trash', it will get double-trashed, so insert it as a draft first + unset( $insert['status'] ); + $post_id = wp_update_post( (object) $insert ); + // now call wp_trash_post so post_meta gets set and any filters get called + wp_trash_post( $post_id ); + } else { + $post_id = wp_update_post( (object) $insert ); + } } @@ -373,12 +398,16 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_ return $post_check; } - if ( $has_media || $has_media_by_url ) { - $media_files = ! empty( $input['media'] ) ? $input['media'] : array(); - $media_urls = ! empty( $input['media_urls'] ) ? $input['media_urls'] : array(); - $media_attrs = ! empty( $input['media_attrs'] ) ? $input['media_attrs'] : array(); - $force_parent_id = $post_id; - $media_results = $this->handle_media_creation_v1_1( $media_files, $media_urls, $media_attrs, $force_parent_id ); + if ( $media_id_string ) { + // Yes - this is really how wp-admin does it. + $wpdb->query( $wpdb->prepare( + "UPDATE $wpdb->posts SET post_parent = %d WHERE post_type = 'attachment' AND ID IN ( $media_id_string )", + $post_id + ) ); + foreach ( $media_results['media_ids'] as $media_id ) { + clean_attachment_cache( $media_id ); + } + clean_post_cache( $post_id ); } // set page template for this post.. @@ -585,6 +614,10 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_ if ( ! empty( $meta->id ) ) { $meta->id = absint( $meta->id ); $existing_meta_item = get_metadata_by_mid( 'post', $meta->id ); + if ( $post_id !== (int) $existing_meta_item->post_id ) { + // Only allow updates for metadata on this post + continue; + } } $unslashed_meta_key = wp_unslash( $meta->key ); // should match what the final key will be @@ -694,7 +727,11 @@ 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-site-settings-endpoint.php */ do_action( 'wpcom_json_api_objects', 'posts' ); - wp_delete_post( $post->ID ); + // we need to call wp_trash_post so that untrash will work correctly for all post types + if ( 'trash' === $post->post_status ) + wp_delete_post( $post->ID ); + else + wp_trash_post( $post->ID ); $status = get_post_status( $post->ID ); if ( false === $status ) { @@ -777,6 +814,6 @@ class WPCOM_JSON_API_Update_Post_v1_1_Endpoint extends WPCOM_JSON_API_Post_v1_1_ $type = get_post_type( $post_id ); } - return ! empty( $type ) && ! in_array( $type, array( 'post', 'page', 'revision' ) ); + return ! empty( $type ) && ! in_array( $type, array( 'post', 'revision' ) ); } } 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 7aeb011d..339d06c5 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 @@ -4,6 +4,8 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos // /sites/%s/posts/new -> $blog_id // /sites/%s/posts/%d -> $blog_id, $post_id function write_post( $path, $blog_id, $post_id ) { + global $wpdb; + $new = $this->api->ends_with( $path, '/new' ); $args = $this->query_args(); @@ -113,9 +115,9 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos } } - if ( function_exists( 'wpcom_switch_to_locale' ) ) { + if ( function_exists( 'wpcom_switch_to_blog_locale' ) ) { // fixes calypso-pre-oss #12476: respect blog locale when creating the post slug - wpcom_switch_to_locale( get_blog_lang_code( $blog_id ) ); + wpcom_switch_to_blog_locale( $blog_id ); } // If date is set, $this->input will set date_gmt, date still needs to be adjusted f @@ -331,8 +333,16 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos $has_media = ! empty( $input['media'] ) ? count( $input['media'] ) : false; $has_media_by_url = ! empty( $input['media_urls'] ) ? count( $input['media_urls'] ) : false; - if ( $new ) { + $media_id_string = ''; + if ( $has_media || $has_media_by_url ) { + $media_files = ! empty( $input['media'] ) ? $input['media'] : array(); + $media_urls = ! empty( $input['media_urls'] ) ? $input['media_urls'] : array(); + $media_attrs = ! empty( $input['media_attrs'] ) ? $input['media_attrs'] : array(); + $media_results = $this->handle_media_creation_v1_1( $media_files, $media_urls, $media_attrs ); + $media_id_string = join( ',', array_filter( array_map( 'absint', $media_results['media_ids'] ) ) ); + } + if ( $new ) { if ( isset( $input['content'] ) && ! has_shortcode( $input['content'], 'gallery' ) && ( $has_media || $has_media_by_url ) ) { switch ( ( $has_media + $has_media_by_url ) ) { case 0 : @@ -340,11 +350,17 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos break; case 1 : // 1 image - make it big - $insert['post_content'] = $input['content'] = "[gallery size=full columns=1]\n\n" . $input['content']; + $insert['post_content'] = $input['content'] = sprintf( + "[gallery size=full ids='%s' columns=1]\n\n", + $media_id_string + ) . $input['content']; break; default : // Several images - 3 column gallery - $insert['post_content'] = $input['content'] = "[gallery]\n\n" . $input['content']; + $insert['post_content'] = $input['content'] = sprintf( + "[gallery ids='%s']\n\n", + $media_id_string + ) . $input['content']; break; } } @@ -360,7 +376,16 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos $insert['edit_date'] = true; } - $post_id = wp_update_post( (object) $insert ); + // this two-step process ensures any changes submitted along with status=trash get saved before trashing + if ( isset( $input['status'] ) && 'trash' === $input['status'] ) { + // if we insert it with status='trash', it will get double-trashed, so insert it as a draft first + unset( $insert['status'] ); + $post_id = wp_update_post( (object) $insert ); + // now call wp_trash_post so post_meta gets set and any filters get called + wp_trash_post( $post_id ); + } else { + $post_id = wp_update_post( (object) $insert ); + } } @@ -374,12 +399,16 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos return $post_check; } - if ( $has_media || $has_media_by_url ) { - $media_files = ! empty( $input['media'] ) ? $input['media'] : array(); - $media_urls = ! empty( $input['media_urls'] ) ? $input['media_urls'] : array(); - $media_attrs = ! empty( $input['media_attrs'] ) ? $input['media_attrs'] : array(); - $force_parent_id = $post_id; - $media_results = $this->handle_media_creation_v1_1( $media_files, $media_urls, $media_attrs, $force_parent_id ); + if ( $media_id_string ) { + // Yes - this is really how wp-admin does it. + $wpdb->query( $wpdb->prepare( + "UPDATE $wpdb->posts SET post_parent = %d WHERE post_type = 'attachment' AND ID IN ( $media_id_string )", + $post_id + ) ); + foreach ( $media_results['media_ids'] as $media_id ) { + clean_attachment_cache( $media_id ); + } + clean_post_cache( $post_id ); } // set page template for this post.. @@ -586,6 +615,10 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos if ( ! empty( $meta->id ) ) { $meta->id = absint( $meta->id ); $existing_meta_item = get_metadata_by_mid( 'post', $meta->id ); + if ( $post_id !== (int) $existing_meta_item->post_id ) { + // Only allow updates for metadata on this post + continue; + } } $unslashed_meta_key = wp_unslash( $meta->key ); // should match what the final key will be @@ -679,6 +712,6 @@ class WPCOM_JSON_API_Update_Post_v1_2_Endpoint extends WPCOM_JSON_API_Update_Pos $type = get_post_type( $post_id ); } - return ! empty( $type ) && ! in_array( $type, array( 'post', 'page', 'revision' ) ); + return ! empty( $type ) && ! in_array( $type, array( 'post', 'revision' ) ); } } diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-site-homepage-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-site-homepage-endpoint.php new file mode 100644 index 00000000..96191fdc --- /dev/null +++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-site-homepage-endpoint.php @@ -0,0 +1,45 @@ +<?php + +class WPCOM_JSON_API_Update_Site_Homepage_Endpoint extends WPCOM_JSON_API_Endpoint { + + function callback( $path = '', $site_id = 0 ) { + $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $site_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 homepage settings', 403 ); + } + + $args = $this->input(); + if ( empty( $args ) || ! is_array( $args ) ) { + return $this->get_current_settings(); + } + + if ( isset( $args['is_page_on_front'] ) ) { + $show_on_front = $args['is_page_on_front'] ? 'page' : 'posts'; + update_option( 'show_on_front', $show_on_front ); + } + if ( isset( $args['page_on_front_id'] ) ) { + update_option( 'page_on_front', $args['page_on_front_id'] ); + } + if ( isset( $args['page_for_posts_id'] ) ) { + update_option( 'page_for_posts', $args['page_for_posts_id'] ); + } + + return $this->get_current_settings(); + } + + function get_current_settings() { + $is_page_on_front = ( get_option( 'show_on_front' ) === 'page' ); + $page_on_front_id = get_option( 'page_on_front' ); + $page_for_posts_id = get_option( 'page_for_posts' ); + + return array( + 'is_page_on_front' => $is_page_on_front, + 'page_on_front_id' => $page_on_front_id, + 'page_for_posts_id' => $page_for_posts_id, + ); + } +} diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-site-logo-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-site-logo-endpoint.php new file mode 100644 index 00000000..89fa9121 --- /dev/null +++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-update-site-logo-endpoint.php @@ -0,0 +1,47 @@ +<?php + +class WPCOM_JSON_API_Update_Site_Logo_Endpoint extends WPCOM_JSON_API_Endpoint { + function callback( $path = '', $site_id = 0 ) { + // Switch to the given blog. + $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $site_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 logo settings', 403 ); + } + + if ( strpos( $path, '/delete' ) ) { + delete_option( 'site_logo' ); + return array(); + } + + $args = $this->input(); + $logo_settings = $this->get_current_settings(); + if ( empty( $args ) || ! is_array( $args ) ) { + return $logo_settings; + } + + if ( isset( $args['id'] ) ) { + $logo_settings['id'] = intval( $args['id'], 10 ); + } + if ( isset( $args['url'] ) ) { + $logo_settings['url'] = $args['url']; + } + if ( isset( $args['url'] ) || isset( $args['id'] ) ) { + update_option( 'site_logo', $logo_settings ); + } + + return $this->get_current_settings(); + } + + function get_current_settings() { + $logo_settings = get_option( 'site_logo' ); + if ( ! is_array( $logo_settings ) ) { + $logo_settings = array(); + } + return $logo_settings; + } +} + 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 b3f72269..979330b2 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 @@ -47,9 +47,8 @@ class WPCOM_JSON_API_Update_Taxonomy_Endpoint extends WPCOM_JSON_API_Taxonomy_En $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 ) { + // the same name is allowed as long as the parents are different + if ( $input['parent'] === $term->parent ) { return new WP_Error( 'duplicate', 'A taxonomy with that name already exists', 400 ); } } 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 index f5915aff..e08e8810 100644 --- 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 @@ -1,5 +1,4 @@ <?php - class WPCOM_JSON_API_Update_User_Endpoint extends WPCOM_JSON_API_Endpoint { function callback( $path = '', $blog_id = 0, $user_id = 0 ) { diff --git a/plugins/jetpack/json-endpoints/class.wpcom-json-api-upload-media-v1-1-endpoint.php b/plugins/jetpack/json-endpoints/class.wpcom-json-api-upload-media-v1-1-endpoint.php index 4c33b270..bd34b606 100644 --- a/plugins/jetpack/json-endpoints/class.wpcom-json-api-upload-media-v1-1-endpoint.php +++ b/plugins/jetpack/json-endpoints/class.wpcom-json-api-upload-media-v1-1-endpoint.php @@ -1,13 +1,19 @@ <?php class WPCOM_JSON_API_Upload_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint { + /** + * @param string $path + * @param int $blog_id + * + * @return array|int|WP_Error|void + */ 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( 'upload_files' ) ) { + if ( ! current_user_can( 'upload_files' ) && ! $this->api->is_authorized_with_upload_token() ) { return new WP_Error( 'unauthorized', 'User cannot upload media.', 403 ); } @@ -16,32 +22,106 @@ class WPCOM_JSON_API_Upload_Media_v1_1_Endpoint extends WPCOM_JSON_API_Endpoint $media_files = ! empty( $input['media'] ) ? $input['media'] : array(); $media_urls = ! empty( $input['media_urls'] ) ? $input['media_urls'] : array(); $media_attrs = ! empty( $input['attrs'] ) ? $input['attrs'] : array(); + if ( empty( $media_files ) && empty( $media_urls ) ) { return new WP_Error( 'invalid_input', 'No media provided in input.' ); } - $create_media = $this->handle_media_creation_v1_1( $media_files, $media_urls, $media_attrs ); - $media_ids = $create_media['media_ids']; - $errors = $create_media['errors']; + // For jetpack sites, we send the media via a different method, because the sync is very different. + $jetpack_sync = Jetpack_Media_Sync::summon( $blog_id ); - $results = array(); - if ( count( $media_ids ) <= 0 ) { - $this->api->output_early( 400, array( 'errors' => $errors ) ); - } else { - foreach ( $media_ids as $media_id ) { - $result = $this->get_media_item_v1_1( $media_id ); - if ( is_wp_error( $result ) ) { - $errors[] = array( 'file' => $media_id, 'error' => $result->get_error_code(), 'message' => $result->get_error_message() ); - } else { - $results[] = $result; - } + $jetpack_media_files = array(); + $other_media_files = array(); + $media_items = array(); + $errors = array(); + + // We're splitting out videos for Jetpack sites + foreach ( $media_files as $media_item ) { + if ( preg_match( '@^video/@', $media_item['type'] ) && $jetpack_sync->is_jetpack_site() ) { + $jetpack_media_files[] = $media_item; + + } else { + $other_media_files[] = $media_item; } - if ( count( $errors ) > 0 ) { - return array( 'media' => $results, 'errors' => $errors ); + } + + // New Jetpack / VideoPress media upload processing + if ( count( $jetpack_media_files ) > 0 ) { + add_filter( 'upload_mimes', array( $this, 'allow_video_uploads' ) ); + + $media_items = $jetpack_sync->upload_media( $jetpack_media_files, $this->api ); + + $errors = $jetpack_sync->get_errors(); + + foreach ( $media_items as & $media_item ) { + // More than likely a post has not been created yet, so we pass in the media item we + // got back from the Jetpack site. + $post = (object) $media_item['post']; + $media_item = $this->get_media_item_v1_1( $media_item['ID'], $post, $media_item['file'] ); + } + } + + // Normal WPCOM upload processing + if ( count( $other_media_files ) > 0 || count( $media_urls ) > 0 ) { + $create_media = $this->handle_media_creation_v1_1( $other_media_files, $media_urls, $media_attrs ); + $media_ids = $create_media['media_ids']; + $errors = $create_media['errors']; + + $media_items = array(); + foreach ( $media_ids as $media_id ) { + $media_items[] = $this->get_media_item_v1_1( $media_id ); + } + } + + if ( count( $media_items ) <= 0 ) { + return $this->api->output_early( 400, array( 'errors' => $errors ) ); + } + + $results = array(); + foreach ( $media_items as $media_item ) { + if ( is_wp_error( $media_item ) ) { + $errors[] = array( 'file' => $media_item['ID'], 'error' => $media_item->get_error_code(), 'message' => $media_item->get_error_message() ); + } else { - return array( 'media' => $results ); + $results[] = $media_item; + } + } + + $response = array( 'media' => $results ); + + if ( count( $errors ) > 0 ) { + $response['errors'] = $errors; + } + + return $response; + } + + /** + * Force to use the WPCOM API instead of proxy back to the Jetpack API if the blog is a paid Jetpack + * blog w/ the VideoPress module enabled AND the uploaded file is a video. + * + * @param int $blog_id + * @return bool + */ + function force_wpcom_request( $blog_id ) { + + // We don't need to do anything if VideoPress is not enabled for the blog. + if ( ! is_videopress_enabled_on_jetpack_blog( $blog_id ) ) { + return false; + } + + // Check to see if the upload is not a video type, if not then return false. + $input = $this->input( true ); + $media_files = ! empty( $input['media'] ) ? $input['media'] : array(); + + foreach ( $media_files as $media_item ) { + if ( ! preg_match( '@^video/@', $media_item['type'] ) ) { + return false; } } + // The API request should be for a blog w/ Jetpack, A valid plan, has VideoPress enabled, + // and is a video file. Let's let it through. + return true; } } diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-comment-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-comment-backup-endpoint.php new file mode 100644 index 00000000..0e4bc256 --- /dev/null +++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-comment-backup-endpoint.php @@ -0,0 +1,52 @@ +<?php + +class Jetpack_JSON_API_Get_Comment_Backup_Endpoint extends Jetpack_JSON_API_Endpoint { + // /sites/%s/comments/%d/backup -> $blog_id, $comment_id + + protected $needed_capabilities = array(); // This endpoint is only accessible using a site token + protected $comment_id; + + function validate_input( $comment_id ) { + if ( empty( $comment_id ) || ! is_numeric( $comment_id ) ) { + return new WP_Error( 'comment_id_not_specified', __( 'You must specify a Comment ID', 'jetpack' ), 400 ); + } + + $this->comment_id = intval( $comment_id ); + + return true; + } + + protected function result() { + $comment = get_comment( $this->comment_id ); + if ( empty( $comment ) ) { + return new WP_Error( 'comment_not_found', __( 'Comment not found', 'jetpack' ), 404 ); + } + + $allowed_keys = array( + 'comment_ID', + 'comment_post_ID', + 'comment_author', + 'comment_author_email', + 'comment_author_url', + 'comment_author_IP', + 'comment_date', + 'comment_date_gmt', + 'comment_content', + 'comment_karma', + 'comment_approved', + 'comment_agent', + 'comment_type', + 'comment_parent', + 'user_id', + ); + + $comment = array_intersect_key( $comment->to_array(), array_flip( $allowed_keys ) ); + $comment_meta = get_comment_meta( $comment['comment_ID'] ); + + return array( + 'comment' => $comment, + 'meta' => is_array( $comment_meta ) ? $comment_meta : array(), + ); + } + +} diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-option-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-option-backup-endpoint.php new file mode 100644 index 00000000..a5d8d3a7 --- /dev/null +++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-option-backup-endpoint.php @@ -0,0 +1,35 @@ +<?php + +class Jetpack_JSON_API_Get_Option_Backup_Endpoint extends Jetpack_JSON_API_Endpoint { + // /sites/%s/options/backup -> $blog_id + + protected $needed_capabilities = array(); // This endpoint is only accessible using a site token + protected $option_names; + + function validate_input( $object ) { + $query_args = $this->query_args(); + + if ( empty( $query_args['name'] ) ) { + return new WP_Error( 'option_name_not_specified', __( 'You must specify an option name', 'jetpack' ), 400 ); + } + + if ( is_array( $query_args['name'] ) ) { + $this->option_names = $query_args['name']; + } else { + $this->option_names = array( $query_args['name'] ); + } + + return true; + } + + protected function result() { + $options = array_map( array( $this, 'get_option_row' ), $this->option_names ); + return array( 'options' => $options ); + } + + private function get_option_row( $name ) { + global $wpdb; + return $wpdb->get_row( $wpdb->prepare( "select * from `{$wpdb->options}` where option_name = %s", $name ) ); + } + +} diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-post-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-post-backup-endpoint.php index c9277cd4..903a16ac 100644 --- a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-post-backup-endpoint.php +++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-post-backup-endpoint.php @@ -3,12 +3,12 @@ class Jetpack_JSON_API_Get_Post_Backup_Endpoint extends Jetpack_JSON_API_Endpoint { // /sites/%s/posts/%d/backup -> $blog_id, $post_id - protected $needed_capabilities = array(); + protected $needed_capabilities = array(); // This endpoint is only accessible using a site token protected $post_id; function validate_input( $post_id ) { if ( empty( $post_id ) || ! is_numeric( $post_id ) ) { - return new WP_Error( 'post_id_not_specified', __( 'You must specify a Post ID', 'jetpack' ) ); + return new WP_Error( 'post_id_not_specified', __( 'You must specify a Post ID', 'jetpack' ), 400 ); } $this->post_id = intval( $post_id ); @@ -19,7 +19,7 @@ class Jetpack_JSON_API_Get_Post_Backup_Endpoint extends Jetpack_JSON_API_Endpoin protected function result() { $post = get_post( $this->post_id ); if ( empty( $post ) ) { - return new WP_Error( 'post_not_found', __( 'Post not found', 'jetpack' ) ); + return new WP_Error( 'post_not_found', __( 'Post not found', 'jetpack' ), 404 ); } return array( diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-term-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-term-backup-endpoint.php new file mode 100644 index 00000000..40d0ab97 --- /dev/null +++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-term-backup-endpoint.php @@ -0,0 +1,32 @@ +<?php + +class Jetpack_JSON_API_Get_Term_Backup_Endpoint extends Jetpack_JSON_API_Endpoint { + // /sites/%s/terms/%d/backup -> $blog_id, $term_id + + protected $needed_capabilities = array(); // This endpoint is only accessible using a site token + protected $term_id; + + function validate_input( $term_id ) { + if ( empty( $term_id ) || ! is_numeric( $term_id ) ) { + return new WP_Error( 'term_id_not_specified', __( 'You must specify a Term ID', 'jetpack' ), 400 ); + } + + $this->term_id = intval( $term_id ); + + return true; + } + + protected function result() { + $term = get_term( $this->term_id ); + if ( empty( $term ) ) { + return new WP_Error( 'term_not_found', __( 'Term not found', 'jetpack' ), 404 ); + } + + return array( + 'term' => (array) $term, + 'meta' => get_term_meta( $this->term_id ), + ); + } + +} + diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-user-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-user-backup-endpoint.php new file mode 100644 index 00000000..12fa5d9b --- /dev/null +++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-user-backup-endpoint.php @@ -0,0 +1,32 @@ +<?php + +class Jetpack_JSON_API_Get_User_Backup_Endpoint extends Jetpack_JSON_API_Endpoint { + // /sites/%s/users/%d/backup -> $blog_id, $user_id + + protected $needed_capabilities = array(); // This endpoint is only accessible using a site token + protected $user_id; + + function validate_input( $user_id ) { + if ( empty( $user_id ) || ! is_numeric( $user_id ) ) { + return new WP_Error( 'user_id_not_specified', __( 'You must specify a User ID', 'jetpack' ), 400 ); + } + + $this->user_id = intval( $user_id ); + + return true; + } + + protected function result() { + $user = get_user_by( 'id', $this->user_id ); + if ( empty( $user ) ) { + return new WP_Error( 'user_not_found', __( 'User not found', 'jetpack' ), 404 ); + } + + return array( + 'user' => (array)$user, + 'meta' => get_user_meta( $user->ID ), + ); + } + +} + 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 229ffc98..04e56550 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 @@ -211,7 +211,7 @@ abstract class Jetpack_JSON_API_Plugins_Endpoint extends Jetpack_JSON_API_Endpoi if ( count( $action_links ) > 0 ) { $dom_doc = new DOMDocument; foreach( $action_links as $action_link ) { - $dom_doc->loadHTML( $action_link ); + $dom_doc->loadHTML( mb_convert_encoding( $action_link, 'HTML-ENTITIES', 'UTF-8' ) ); $link_elements = $dom_doc->getElementsByTagName( 'a' ); if ( $link_elements->length == 0 ) { continue; 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 22394029..c18a754f 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 @@ -245,7 +245,7 @@ class Jetpack_JSON_API_Plugins_Modify_Endpoint extends Jetpack_JSON_API_Plugins_ * * @param array $plugin Plugin data * @param array $plugin Array of plugin objects - * @param bool $updated_attempted false for the first update, true subsequently + * @param bool $update_attempted false for the first update, true subsequently */ do_action( 'jetpack_pre_plugin_upgrade_translations', $plugin, $this->plugins, $update_attempted ); diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-connect-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-connect-endpoint.php new file mode 100644 index 00000000..8b758559 --- /dev/null +++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-connect-endpoint.php @@ -0,0 +1,31 @@ +<?php + +class Jetpack_JSON_API_User_Connect_Endpoint extends Jetpack_JSON_API_Endpoint { + + protected $needed_capabilities = 'create_users'; + + private $user_id; + private $user_token; + + function result() { + Jetpack::update_user_token( $this->user_id, sprintf( '%s.%d', $this->user_token, $this->user_id ), false ); + return array( 'success' => Jetpack::is_user_connected( $this->user_id ) ); + } + + function validate_input( $user_id ) { + $input = $this->input(); + if ( ! isset( $user_id ) ) { + return new WP_Error( 'input_error', __( 'user_id is required', 'jetpack' ) ); + } + $this->user_id = $user_id; + if ( Jetpack::is_user_connected( $this->user_id ) ) { + return new WP_Error( 'user_already_connected', __( 'The user is already connected', 'jetpack' ) ); + } + if ( ! isset( $input['user_token'] ) ) { + return new WP_Error( 'input_error', __( 'user_token is required', 'jetpack' ) ); + } + $this->user_token = sanitize_text_field( $input[ 'user_token'] ); + return parent::validate_input( $user_id ); + } + +} diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php new file mode 100644 index 00000000..07d1c219 --- /dev/null +++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php @@ -0,0 +1,61 @@ +<?php + +class Jetpack_JSON_API_User_Create_Endpoint extends Jetpack_JSON_API_Endpoint { + + protected $needed_capabilities = 'create_users'; + + private $user_data; + + function result() { + return $this->create_or_get_user(); + } + + function validate_input( $object ) { + $this->user_data = $this->input(); + if ( empty( $this->user_data ) ) { + return new WP_Error( 'input_error', __( 'user_data is required', 'jetpack' ) ); + } + if ( ! isset( $this->user_data[ 'email' ] ) ) { + return new WP_Error( 'input_error', __( 'user email is required', 'jetpack' ) ); + } + if ( ! isset( $this->user_data[ 'login' ] ) ) { + return new WP_Error( 'input_error', __( 'user login is required', 'jetpack' ) ); + } + return parent::validate_input( $object ); + } + + function create_or_get_user() { + require_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php'; + + // Check for an existing user + $user = get_user_by( 'email', $this->user_data['email'] ); + + if ( ! $user ) { + // We modify the input here to mimick the same call structure of the update user endpoint. + $this->user_data = (object) $this->user_data; + $roles = (array) $this->user_data->roles; + $this->user_data->role = array_pop( $roles ); + $this->user_data->url = $this->user_data->roles; + $this->user_data->display_name = $this->user_data->name; + $this->user_data->description = ''; + $user = Jetpack_SSO_Helpers::generate_user( $this->user_data ); + } + + if ( ! $user ) { + return false; + } + + return $this->get_user( $user->ID ); + } + + 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; + } + +} 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 bf8d23ea..918349ce 100644 --- a/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php +++ b/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php @@ -1134,9 +1134,60 @@ new Jetpack_JSON_API_Cron_Unschedule_Endpoint( array( ) ); // BACKUPS -require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-post-backup-endpoint.php' ); + +// GET /sites/%s/comments/%d/backup +require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-comment-backup-endpoint.php' ); +new Jetpack_JSON_API_Get_Comment_Backup_Endpoint( array( + 'description' => 'Fetch a backup of a comment, along with all of its metadata', + 'group' => '__do_not_document', + 'method' => 'GET', + 'path' => '/sites/%s/comments/%d/backup', + 'stat' => 'comments:1:backup', + 'allow_jetpack_site_auth' => true, + 'path_labels' => array( + '$site' => '(int|string) The site ID, The site domain', + '$post' => '(int) The comment ID', + ), + 'response_format' => array( + 'comment' => '(array) Comment table row', + 'meta' => '(array) Associative array of key/value commentmeta data', + ), + 'example_request_data' => array( + 'headers' => array( + 'authorization' => 'Bearer YOUR_API_TOKEN' + ), + ), + 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/comments/1/backup' +) ); + +// GET /sites/%s/options/backup +require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-option-backup-endpoint.php' ); +new Jetpack_JSON_API_Get_Option_Backup_Endpoint( array( + 'description' => 'Fetch a backup of an option', + 'group' => '__do_not_document', + 'method' => 'GET', + 'path' => '/sites/%s/options/backup', + 'stat' => 'options:backup', + 'allow_jetpack_site_auth' => true, + 'path_labels' => array( + '$site' => '(int|string) The site ID, The site domain', + ), + 'query_parameters' => array( + 'name' => '(string|array) One or more option names to include in the backup', + ), + 'response_format' => array( + 'options' => '(array) Associative array of option_name => option_value entries', + ), + 'example_request_data' => array( + 'headers' => array( + 'authorization' => 'Bearer YOUR_API_TOKEN' + ) + ), + 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/options/backup' +) ); // GET /sites/%s/posts/%d/backup +require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-post-backup-endpoint.php' ); new Jetpack_JSON_API_Get_Post_Backup_Endpoint( array( 'description' => 'Fetch a backup of a post, along with all of its metadata', 'group' => '__do_not_document', @@ -1159,3 +1210,133 @@ new Jetpack_JSON_API_Get_Post_Backup_Endpoint( array( ), 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/posts/1/backup' ) ); + +// GET /sites/%s/terms/%d/backup +require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-term-backup-endpoint.php' ); +new Jetpack_JSON_API_Get_Term_Backup_Endpoint( array( + 'description' => 'Fetch a backup of a term, along with all of its metadata', + 'group' => '__do_not_document', + 'method' => 'GET', + 'path' => '/sites/%s/terms/%d/backup', + 'stat' => 'terms:1:backup', + 'allow_jetpack_site_auth' => true, + 'path_labels' => array( + '$site' => '(int|string) The site ID, The site domain', + '$term' => '(int) The term ID', + ), + 'response_format' => array( + 'term' => '(array) Term table row', + 'meta' => '(array) Metadata associated with the term', + ), + 'example_request_data' => array( + 'headers' => array( + 'authorization' => 'Bearer YOUR_API_TOKEN' + ), + ), + 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/terms/1/backup' +) ); + +// GET /sites/%s/users/%d/backup +require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-user-backup-endpoint.php' ); +new Jetpack_JSON_API_Get_User_Backup_Endpoint( array( + 'description' => 'Fetch a backup of a user, along with all of its metadata', + 'group' => '__do_not_document', + 'method' => 'GET', + 'path' => '/sites/%s/users/%d/backup', + 'stat' => 'users:1:backup', + 'allow_jetpack_site_auth' => true, + 'path_labels' => array( + '$site' => '(int|string) The site ID, The site domain', + '$user' => '(int) The user ID', + ), + 'response_format' => array( + 'user' => '(array) User table row', + 'meta' => '(array) Associative array of key/value usermeta data', + ), + 'example_request_data' => array( + 'headers' => array( + 'authorization' => 'Bearer YOUR_API_TOKEN' + ), + ), + 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/users/1/backup' +) ); + +// USERS + +require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-user-connect-endpoint.php' ); + +// POST /sites/%s/users/%d/connect +new Jetpack_JSON_API_User_Connect_Endpoint( array( + 'description' => 'Creates or returns a new user given profile data', + 'group' => '__do_not_document', + 'method' => 'POST', + 'path' => '/sites/%s/users/%d/connect', + 'stat' => 'users:connect', + 'allow_jetpack_site_auth' => true, + 'path_labels' => array( + '$site' => '(int|string) The site ID, The site domain', + '$user_id' => '(int) The site user ID to connect', + ), + 'request_format' => array( + 'user_token' => '(string) The user token', + ), + 'response_format' => array( + 'success' => '(bool) Was the user connected', + ), + 'example_request_data' => array( + 'headers' => array( + 'authorization' => 'Bearer YOUR_API_TOKEN', + ), + 'body' => array( + 'user_token' => 'XDH55jndskjf3klh3', + ) + ), + 'example_response' => '{ + "success" => true + }', + 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/users/6/connect' +) ); + +require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-user-create-endpoint.php' ); + +// POST /sites/%s/users/create +new Jetpack_JSON_API_User_Create_Endpoint( array( + 'description' => 'Creates or returns a new user given profile data', + 'group' => '__do_not_document', + 'method' => 'POST', + 'path' => '/sites/%s/users/create', + 'stat' => 'users:create', + 'allow_jetpack_site_auth' => true, + 'path_labels' => array( + '$site' => '(int|string) The site ID, The site domain', + ), + 'request_format' => WPCOM_JSON_API_Site_User_Endpoint::$user_format, + 'response_format' => WPCOM_JSON_API_Site_User_Endpoint::$user_format, + 'example_request_data' => array( + 'headers' => array( + 'authorization' => 'Bearer YOUR_API_TOKEN' + ), + 'body' => array( + 'roles' => array( + array( + 'administrator', + ) + ), + 'first_name' => 'John', + 'last_name' => 'Doe', + 'email' => 'john.doe@example.wordpress.org', + ) + ), + 'example_response' => '{ + "ID": 18342963, + "login": "binarysmash" + "email": false, + "name": "binarysmash", + "URL": "http:\/\/binarysmash.wordpress.com", + "avatar_URL": "http:\/\/0.gravatar.com\/avatar\/a178ebb1731d432338e6bb0158720fcc?s=96&d=identicon&r=G", + "profile_URL": "http:\/\/en.gravatar.com\/binarysmash", + "roles": [ "administrator" ] + }', + 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/users/create' + +) ); |