From c155cac1cb13c271b55f1ca2f575098fb2cb41e9 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 20 Dec 2015 16:33:21 -0600 Subject: [PATCH 001/318] Allow the `comment_author_IP` value to be set --- lib/endpoints/class-wp-rest-comments-controller.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 1ac353435c..93d77d3a0c 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -173,7 +173,11 @@ public function create_item( $request ) { if ( ! isset( $prepared_comment['comment_author_url'] ) ) { $prepared_comment['comment_author_url'] = ''; } - $prepared_comment['comment_author_IP'] = '127.0.0.1'; + if ( isset( $prepared_comment['comment_author_IP'] ) ) { + $prepared_comment['comment_author_IP'] = ( filter_var( $prepared_comment['comment_author_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) ? $prepared_comment['comment_author_IP'] : '127.0.0.1'; + } else { + $prepared_comment['comment_author_IP'] = '127.0.0.1'; + } $prepared_comment['comment_agent'] = ''; $prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment ); From 04b3fcd4c9be00653e7c888c1df9f1a6d4797138 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 20 Dec 2015 17:02:07 -0600 Subject: [PATCH 002/318] Move the comment_author_IP validation to the `prepare_item_for_database` method and return error if invalid --- lib/endpoints/class-wp-rest-comments-controller.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 93d77d3a0c..eaf7486c27 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -173,9 +173,7 @@ public function create_item( $request ) { if ( ! isset( $prepared_comment['comment_author_url'] ) ) { $prepared_comment['comment_author_url'] = ''; } - if ( isset( $prepared_comment['comment_author_IP'] ) ) { - $prepared_comment['comment_author_IP'] = ( filter_var( $prepared_comment['comment_author_IP'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) ? $prepared_comment['comment_author_IP'] : '127.0.0.1'; - } else { + if ( ! isset( $prepared_comment['comment_author_IP'] ) ) { $prepared_comment['comment_author_IP'] = '127.0.0.1'; } $prepared_comment['comment_agent'] = ''; @@ -703,6 +701,14 @@ protected function prepare_item_for_database( $request ) { $prepared_comment['comment_author_url'] = $request['author_url']; } + if ( isset( $request['author_ip'] ) ) { + if ( filter_var( $request['author_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) { + $prepared_comment['comment_author_IP'] = $request['author_ip']; + } else { + return new WP_Error( 'rest_invalid_author_ip', __( 'The IP address you provided is invalid.' ), array( 'status' => 400 ) ); + } + } + if ( isset( $request['type'] ) ) { $prepared_comment['comment_type'] = $request['type']; } From fac39453bdb47ada09c7de83d7d0eda2b650cee7 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 20 Dec 2015 19:56:36 -0600 Subject: [PATCH 003/318] Prevent fatal error when `prepare_item_for_database` returns a WP_Error --- lib/endpoints/class-wp-rest-comments-controller.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index eaf7486c27..c2ca9bef44 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -146,6 +146,9 @@ public function create_item( $request ) { } $prepared_comment = $this->prepare_item_for_database( $request ); + if ( is_wp_error( $prepared_comment ) ) { + return $prepared_comment; + } // Setting remaining values before wp_insert_comment so we can // use wp_allow_comment(). @@ -244,6 +247,9 @@ public function update_item( $request ) { } $prepared_args = $this->prepare_item_for_database( $request ); + if ( is_wp_error( $prepared_args ) ) { + return $prepared_args; + } if ( empty( $prepared_args ) && isset( $request['status'] ) ) { // Only the comment status is being changed. @@ -705,7 +711,7 @@ protected function prepare_item_for_database( $request ) { if ( filter_var( $request['author_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) { $prepared_comment['comment_author_IP'] = $request['author_ip']; } else { - return new WP_Error( 'rest_invalid_author_ip', __( 'The IP address you provided is invalid.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_comment_invalid_author_ip', __( 'The IP address you provided is invalid.' ), array( 'status' => 400 ) ); } } From 18bee8c339d086f22cb61af28447726086e9f28e Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 20 Dec 2015 19:56:59 -0600 Subject: [PATCH 004/318] Add unit tests for creating a comment with `author_ip` --- tests/test-rest-comments-controller.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 8734b2518c..4cdf6a9788 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -539,7 +539,7 @@ public function test_create_comment_status_without_permission() { $this->assertErrorResponse( 'rest_comment_invalid_status', $response, 403 ); } - public function test_create_comment_with_status() { + public function test_create_comment_with_status_and_IP() { $post_id = $this->factory->post->create(); wp_set_current_user( $this->admin_id ); @@ -547,6 +547,7 @@ public function test_create_comment_with_status() { 'post' => $post_id, 'author_name' => 'Comic Book Guy', 'author_email' => 'cbg@androidsdungeon.com', + 'author_ip' => '139.130.4.5', 'author_url' => 'http://androidsdungeon.com', 'content' => 'Worst Comment Ever!', 'status' => 'approved', @@ -561,6 +562,26 @@ public function test_create_comment_with_status() { $data = $response->get_data(); $this->assertEquals( 'approved', $data['status'] ); + $this->assertEquals( '139.130.4.5', $data['author_ip'] ); + } + + public function test_create_comment_invalid_author_IP() { + wp_set_current_user( $this->admin_id ); + + $params = array( + 'author_name' => 'Comic Book Guy', + 'author_email' => 'cbg@androidsdungeon.com', + 'author_url' => 'http://androidsdungeon.com', + 'author_ip' => '867.5309', + 'content' => 'Worst Comment Ever!', + 'status' => 'approved', + ); + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_comment_invalid_author_ip', $response, 400 ); } public function test_create_comment_no_post_id() { From 153bb8ddcb6cb613bbd374fe24bd5d0cec2599cc Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Sun, 17 Jan 2016 22:15:38 +1030 Subject: [PATCH 005/318] Ensure comment content is not empty --- lib/endpoints/class-wp-rest-comments-controller.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 949accadfa..ee6347729b 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -177,6 +177,10 @@ public function create_item( $request ) { $prepared_comment['comment_agent'] = ''; $prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment ); + if ( '' == $prepared_comment['comment_content'] ) { + return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 500 ) ); + } + /** * Filter a comment before it is inserted via the REST API. * From 38411ce48821bf1cc22e3b182daf5268e086723b Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Sun, 17 Jan 2016 22:15:57 +1030 Subject: [PATCH 006/318] Use wp_new_comment instead of wp_insert_comment --- lib/endpoints/class-wp-rest-comments-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index ee6347729b..e53c07c9ac 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -184,14 +184,14 @@ public function create_item( $request ) { /** * Filter a comment before it is inserted via the REST API. * - * Allows modification of the comment right before it is inserted via `wp_insert_comment`. + * Allows modification of the comment right before it is inserted via `wp_new_comment`. * - * @param array $prepared_comment The prepared comment data for `wp_insert_comment`. + * @param array $prepared_comment The prepared comment data for `wp_new_comment`. * @param WP_REST_Request $request Request used to insert the comment. */ $prepared_comment = apply_filters( 'rest_pre_insert_comment', $prepared_comment, $request ); - $comment_id = wp_insert_comment( $prepared_comment ); + $comment_id = wp_new_comment( $prepared_comment ); if ( ! $comment_id ) { return new WP_Error( 'rest_comment_failed_create', __( 'Creating comment failed.' ), array( 'status' => 500 ) ); } From 38811f224a154e073e880f41b161b7f668ca6c15 Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Sun, 17 Jan 2016 22:36:14 +1030 Subject: [PATCH 007/318] author_name and author_email validation for comments --- lib/endpoints/class-wp-rest-comments-controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index e53c07c9ac..b7c417dbd6 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -181,6 +181,14 @@ public function create_item( $request ) { return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 500 ) ); } + if ( get_option( 'require_name_email' ) && ! isset( $user ) ) { + if ( 6 > strlen( $prepared_comment['comment_author_email'] ) || '' == $prepared_comment['comment_author'] ) { + return new WP_Error( 'rest_require_name_email', __( 'Required fields (name, email) missing.' ), array( 'status' => 500 ) ); + } elseif ( ! is_email( $prepared_comment['comment_author_email'] ) ) { + return new WP_Error( 'rest_require_valid_email', __( 'Valid email address required.' ), array( 'status' => 500 ) ); + } + } + /** * Filter a comment before it is inserted via the REST API. * From 7180a908d041979782c6526d90f62081a031c339 Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Mon, 18 Jan 2016 22:33:32 +1030 Subject: [PATCH 008/318] Fix test_get_additional_field_registration --- tests/test-rest-comments-controller.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index d17be49545..ecbcf9d00f 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1006,7 +1006,7 @@ public function test_get_additional_field_registration() { $request = new WP_REST_Request( 'POST', '/wp/v2/comments/' . $this->approved_id ); $request->set_body_params(array( 'my_custom_int' => 123, - 'content' => 'abc', + 'content' => 'abc', )); wp_set_current_user( 1 ); @@ -1016,8 +1016,11 @@ public function test_get_additional_field_registration() { $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); $request->set_body_params(array( 'my_custom_int' => 123, - 'title' => 'hello', - 'post' => $this->post_id, + 'title' => 'hello', + 'post' => $this->post_id, + 'content' => 'abc2', + 'author_name' => 'Comic Book Guy', + 'author_email' => 'cbg@androidsdungeon.com', )); $response = $this->server->dispatch( $request ); From 4730c0a908dad96bfb2aabea2659f81271a06778 Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Mon, 18 Jan 2016 22:54:28 +1030 Subject: [PATCH 009/318] Revert to using wp_insert_comment due to issue with wp_new_comment requiring a post_id --- lib/endpoints/class-wp-rest-comments-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index b7c417dbd6..3e91214d90 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -192,14 +192,14 @@ public function create_item( $request ) { /** * Filter a comment before it is inserted via the REST API. * - * Allows modification of the comment right before it is inserted via `wp_new_comment`. + * Allows modification of the comment right before it is inserted via `wp_insert_comment`. * - * @param array $prepared_comment The prepared comment data for `wp_new_comment`. + * @param array $prepared_comment The prepared comment data for `wp_insert_comment`. * @param WP_REST_Request $request Request used to insert the comment. */ $prepared_comment = apply_filters( 'rest_pre_insert_comment', $prepared_comment, $request ); - $comment_id = wp_new_comment( $prepared_comment ); + $comment_id = wp_insert_comment( $prepared_comment ); if ( ! $comment_id ) { return new WP_Error( 'rest_comment_failed_create', __( 'Creating comment failed.' ), array( 'status' => 500 ) ); } From 25ec55423f1fc03d68795a4c5ffdbb8b8d8d43ae Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Mon, 18 Jan 2016 22:57:56 +1030 Subject: [PATCH 010/318] Add comments and additional validation --- .../class-wp-rest-comments-controller.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 3e91214d90..6ef86ca455 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -147,11 +147,19 @@ public function create_item( $request ) { $prepared_comment = $this->prepare_item_for_database( $request ); + // Check that comment has content + if ( '' == $prepared_comment['comment_content'] ) { + return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 500 ) ); + } + // Setting remaining values before wp_insert_comment so we can // use wp_allow_comment(). - if ( ! isset( $prepared_comment['comment_date_gmt'] ) ) { + if ( empty( $prepared_comment['comment_date_gmt'] ) ) { $prepared_comment['comment_date_gmt'] = current_time( 'mysql', true ); } + if ( empty( $prepared_comment['comment_date'] ) ) { + $prepared_comment['comment_date'] = current_time('mysql'); + } // Set author data if the user's logged in $missing_author = empty( $prepared_comment['user_id'] ) @@ -177,10 +185,7 @@ public function create_item( $request ) { $prepared_comment['comment_agent'] = ''; $prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment ); - if ( '' == $prepared_comment['comment_content'] ) { - return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 500 ) ); - } - + // Check author name and email if required if ( get_option( 'require_name_email' ) && ! isset( $user ) ) { if ( 6 > strlen( $prepared_comment['comment_author_email'] ) || '' == $prepared_comment['comment_author'] ) { return new WP_Error( 'rest_require_name_email', __( 'Required fields (name, email) missing.' ), array( 'status' => 500 ) ); From 3ccdb64c91557a77d868d5e9db208a7c35446188 Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Tue, 19 Jan 2016 21:44:57 +1030 Subject: [PATCH 011/318] Fix formatting issues --- lib/endpoints/class-wp-rest-comments-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 6ef86ca455..113ab76e52 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -148,7 +148,7 @@ public function create_item( $request ) { $prepared_comment = $this->prepare_item_for_database( $request ); // Check that comment has content - if ( '' == $prepared_comment['comment_content'] ) { + if ( empty( $prepared_comment['comment_content'] ) ) { return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 500 ) ); } @@ -158,7 +158,7 @@ public function create_item( $request ) { $prepared_comment['comment_date_gmt'] = current_time( 'mysql', true ); } if ( empty( $prepared_comment['comment_date'] ) ) { - $prepared_comment['comment_date'] = current_time('mysql'); + $prepared_comment['comment_date'] = current_time( 'mysql' ); } // Set author data if the user's logged in @@ -187,7 +187,7 @@ public function create_item( $request ) { // Check author name and email if required if ( get_option( 'require_name_email' ) && ! isset( $user ) ) { - if ( 6 > strlen( $prepared_comment['comment_author_email'] ) || '' == $prepared_comment['comment_author'] ) { + if ( 6 > strlen( $prepared_comment['comment_author_email'] ) || empty( $prepared_comment['comment_author'] ) ) { return new WP_Error( 'rest_require_name_email', __( 'Required fields (name, email) missing.' ), array( 'status' => 500 ) ); } elseif ( ! is_email( $prepared_comment['comment_author_email'] ) ) { return new WP_Error( 'rest_require_valid_email', __( 'Valid email address required.' ), array( 'status' => 500 ) ); From be81e6705db546e6320bb6dce84b2e30927a10cc Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Tue, 19 Jan 2016 22:12:55 +1030 Subject: [PATCH 012/318] Add tests to cover additional validation --- .../class-wp-rest-comments-controller.php | 6 ++-- tests/test-rest-comments-controller.php | 35 +++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 113ab76e52..ba1e0708b0 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -149,7 +149,7 @@ public function create_item( $request ) { // Check that comment has content if ( empty( $prepared_comment['comment_content'] ) ) { - return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 500 ) ); + return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 400 ) ); } // Setting remaining values before wp_insert_comment so we can @@ -188,9 +188,9 @@ public function create_item( $request ) { // Check author name and email if required if ( get_option( 'require_name_email' ) && ! isset( $user ) ) { if ( 6 > strlen( $prepared_comment['comment_author_email'] ) || empty( $prepared_comment['comment_author'] ) ) { - return new WP_Error( 'rest_require_name_email', __( 'Required fields (name, email) missing.' ), array( 'status' => 500 ) ); + return new WP_Error( 'rest_require_valid_comment', __( 'Required fields (name, email) missing.' ), array( 'status' => 400 ) ); } elseif ( ! is_email( $prepared_comment['comment_author_email'] ) ) { - return new WP_Error( 'rest_require_valid_email', __( 'Valid email address required.' ), array( 'status' => 500 ) ); + return new WP_Error( 'rest_require_valid_comment', __( 'Valid email address required.' ), array( 'status' => 400 ) ); } } diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index ecbcf9d00f..ccbc8c9669 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -625,6 +625,41 @@ public function test_create_comment_no_post_id() { $this->assertEquals( 201, $response->get_status() ); } + public function test_create_comment_without_content() { + wp_set_current_user( $this->subscriber_id ); + + $params = array( + 'post' => $this->post_id, + 'author_name' => 'Homer Jay Simpson', + 'author_email' => 'chunkylover53@aol.com', + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_require_valid_comment', $response, 400 ); + } + + public function test_create_comment_without_author() { + add_filter('comment_flood_filter', '__return_false'); + wp_set_current_user( 0 ); + update_option( 'require_name_email', 1 ); + + $params = array( + 'post' => $this->post_id, + 'content' => 'No TV and no beer makes Homer something something.', + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_require_valid_comment', $response, 400 ); + } + public function test_create_item_duplicate() { $this->markTestSkipped( 'Needs to be revisited after wp_die handling is added' ); $original_id = $this->factory->comment->create( From 70a527e0fe8dadbe86defe2706fe4646a2e62de2 Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Tue, 19 Jan 2016 22:21:43 +1030 Subject: [PATCH 013/318] Fix formatting issues in test --- tests/test-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index ccbc8c9669..aaa1344df4 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -643,7 +643,7 @@ public function test_create_comment_without_content() { } public function test_create_comment_without_author() { - add_filter('comment_flood_filter', '__return_false'); + add_filter( 'comment_flood_filter', '__return_false' ); wp_set_current_user( 0 ); update_option( 'require_name_email', 1 ); From 3e969574cfcae6c636df31c2850430f9cc896173 Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Tue, 26 Jan 2016 10:40:22 +1030 Subject: [PATCH 014/318] Add full-stop to comments --- lib/endpoints/class-wp-rest-comments-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index ba1e0708b0..0ce93e8cd8 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -147,7 +147,7 @@ public function create_item( $request ) { $prepared_comment = $this->prepare_item_for_database( $request ); - // Check that comment has content + // Check that comment has content. if ( empty( $prepared_comment['comment_content'] ) ) { return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 400 ) ); } @@ -161,7 +161,7 @@ public function create_item( $request ) { $prepared_comment['comment_date'] = current_time( 'mysql' ); } - // Set author data if the user's logged in + // Set author data if the user's logged in. $missing_author = empty( $prepared_comment['user_id'] ) && empty( $prepared_comment['comment_author'] ) && empty( $prepared_comment['comment_author_email'] ) @@ -185,7 +185,7 @@ public function create_item( $request ) { $prepared_comment['comment_agent'] = ''; $prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment ); - // Check author name and email if required + // Check author name and email if required. if ( get_option( 'require_name_email' ) && ! isset( $user ) ) { if ( 6 > strlen( $prepared_comment['comment_author_email'] ) || empty( $prepared_comment['comment_author'] ) ) { return new WP_Error( 'rest_require_valid_comment', __( 'Required fields (name, email) missing.' ), array( 'status' => 400 ) ); From 5b29ac79d7093bb7e28a721bf11e64030cb4c7b0 Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Tue, 26 Jan 2016 10:43:27 +1030 Subject: [PATCH 015/318] Remove author_email length check --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 0ce93e8cd8..91166f86e6 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -187,7 +187,7 @@ public function create_item( $request ) { // Check author name and email if required. if ( get_option( 'require_name_email' ) && ! isset( $user ) ) { - if ( 6 > strlen( $prepared_comment['comment_author_email'] ) || empty( $prepared_comment['comment_author'] ) ) { + if ( empty( $prepared_comment['comment_author_email'] ) || empty( $prepared_comment['comment_author'] ) ) { return new WP_Error( 'rest_require_valid_comment', __( 'Required fields (name, email) missing.' ), array( 'status' => 400 ) ); } elseif ( ! is_email( $prepared_comment['comment_author_email'] ) ) { return new WP_Error( 'rest_require_valid_comment', __( 'Valid email address required.' ), array( 'status' => 400 ) ); From c75d8f43f04cb085432dd99ee9591812c8b979cf Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Thu, 17 Mar 2016 10:54:13 -0700 Subject: [PATCH 016/318] Fix incorrect context mention in WP_Error --- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index d2361bb5c1..e92fc6c6c0 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -192,7 +192,7 @@ public function get_item_permissions_check( $request ) { } if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) { - return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this resource with view context.' ), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this resource with edit context.' ), array( 'status' => rest_authorization_required_code() ) ); } else if ( ! count_user_posts( $id, $types ) && ! current_user_can( 'edit_user', $id ) && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this resource.' ), array( 'status' => rest_authorization_required_code() ) ); } From ba8eb1372ce83e82184a58db525b14fa835b9d53 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Thu, 17 Mar 2016 11:38:35 -0700 Subject: [PATCH 017/318] Use `show_in_rest` to determine "public" post types to check Whether or not a user should appear in the REST API should be determined by whether they have any posts that appear in the API, not public posts generally. From 71800a95cc722a85025aeb1fe671665b369c3de6 --- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index d2361bb5c1..9139f5d8a7 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -181,7 +181,7 @@ public function get_item_permissions_check( $request ) { $id = (int) $request['id']; $user = get_userdata( $id ); - $types = get_post_types( array( 'public' => true ), 'names' ); + $types = get_post_types( array( 'show_in_rest' => true ), 'names' ); if ( empty( $id ) || empty( $user->ID ) ) { return new WP_Error( 'rest_user_invalid_id', __( 'Invalid resource id.' ), array( 'status' => 404 ) ); From 54a50c384185558e3f2179dc82d2b43966593e59 Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 18 Mar 2016 14:49:48 -0400 Subject: [PATCH 018/318] Added Unit Test for tags pagination --- tests/test-rest-tags-controller.php | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/test-rest-tags-controller.php b/tests/test-rest-tags-controller.php index 8cd9ce7a63..d41c29c461 100644 --- a/tests/test-rest-tags-controller.php +++ b/tests/test-rest-tags-controller.php @@ -224,6 +224,45 @@ public function test_get_items_post_args() { $this->assertEquals( 'DC', $data[0]['name'] ); } + public function test_get_terms_post_args_paging() { + $post_id = $this->factory->post->create(); + $tag_ids = array(); + + for ( $i = 0; $i < 30; $i++ ) { + $tag_ids[] = $this->factory->tag->create( array( + 'name' => "Tag {$i}", + ) ); + } + wp_set_object_terms( $post_id, $tag_ids, 'post_tag' ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/tags' ); + $request->set_param( 'post', $post_id ); + $request->set_param( 'page', 1 ); + $request->set_param( 'per_page', 15 ); + $request->set_param( 'orderby', 'id' ); + $response = $this->server->dispatch( $request ); + $tags = $response->get_data(); + + $i = 0; + foreach ( $tags as $tag ) { + $this->assertEquals( $tag['name'], "Tag {$i}" ); + $i++; + } + + $request = new WP_REST_Request( 'GET', '/wp/v2/tags' ); + $request->set_param( 'post', $post_id ); + $request->set_param( 'page', 2 ); + $request->set_param( 'per_page', 15 ); + $request->set_param( 'orderby', 'id' ); + $response = $this->server->dispatch( $request ); + $tags = $response->get_data(); + + foreach ( $tags as $tag ) { + $this->assertEquals( $tag['name'], "Tag {$i}" ); + $i++; + } + } + public function test_get_items_post_empty() { $post_id = $this->factory->post->create(); From 56b6b87f090db4411bc692502cb59b8f98a65042 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 18 Mar 2016 16:35:09 -0400 Subject: [PATCH 019/318] Expand curies for testing purposes --- tests/bootstrap.php | 18 ++++++++++++++++++ ...test-rest-post-type-controller-testcase.php | 4 +++- tests/test-rest-post-types-controller.php | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/bootstrap.php b/tests/bootstrap.php index febc586515..ce85a480e4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -45,6 +45,24 @@ function _manually_load_plugin() { if ( ! class_exists( 'WP_Test_REST_TestCase' ) ) { require_once dirname( __FILE__ ) . '/class-wp-test-rest-testcase.php'; } +function test_rest_expand_compact_links( $links ) { + if ( empty( $links['curies'] ) ) { + return $links; + } + foreach ( $links as $rel => $links_array ) { + if ( ! strpos( $rel, ':' ) ) { + continue; + } + + $name = explode( ':', $rel ); + + $curie = wp_list_filter( $links['curies'], array( 'name' => $name[0] ) ); + $full_uri = str_replace( '{rel}', $name[1], $curie[0]['href'] ); + $links[ $full_uri ] = $links_array; + unset( $links[ $rel ] ); + } + return $links; +} require_once dirname( __FILE__ ) . '/class-wp-test-rest-controller-testcase.php'; require_once dirname( __FILE__ ) . '/class-wp-test-rest-post-type-controller-testcase.php'; diff --git a/tests/class-wp-test-rest-post-type-controller-testcase.php b/tests/class-wp-test-rest-post-type-controller-testcase.php index a1686fd531..fb4324a1e0 100644 --- a/tests/class-wp-test-rest-post-type-controller-testcase.php +++ b/tests/class-wp-test-rest-post-type-controller-testcase.php @@ -156,6 +156,8 @@ protected function check_post_data( $post, $data, $context, $links ) { // test links if ( $links ) { + + $links = test_rest_expand_compact_links( $links ); $post_type = get_post_type_object( $data['type'] ); $this->assertEquals( $links['self'][0]['href'], rest_url( 'wp/v2/' . $post_type->rest_base . '/' . $data['id'] ) ); $this->assertEquals( $links['collection'][0]['href'], rest_url( 'wp/v2/' . $post_type->rest_base ) ); @@ -212,7 +214,7 @@ protected function check_get_posts_response( $response, $context = 'view' ) { $links = $data['_links']; foreach ( $links as &$links_array ) { foreach ( $links_array as &$link ) { - $attributes = array_diff_key( $link, array( 'href' => 1 ) ); + $attributes = array_diff_key( $link, array( 'href' => 1, 'name' => 1 ) ); $link = array_diff_key( $link, $attributes ); $link['attributes'] = $attributes; } diff --git a/tests/test-rest-post-types-controller.php b/tests/test-rest-post-types-controller.php index bbe5e739bf..8cc7b2c2f6 100644 --- a/tests/test-rest-post-types-controller.php +++ b/tests/test-rest-post-types-controller.php @@ -150,6 +150,8 @@ protected function check_post_type_obj( $context, $post_type_obj, $data, $links $this->assertEquals( $post_type_obj->name, $data['slug'] ); $this->assertEquals( $post_type_obj->description, $data['description'] ); $this->assertEquals( $post_type_obj->hierarchical, $data['hierarchical'] ); + + $links = test_rest_expand_compact_links( $links ); $this->assertEquals( rest_url( 'wp/v2/types' ), $links['collection'][0]['href'] ); $this->assertArrayHasKey( 'https://api.w.org/items', $links ); if ( 'edit' === $context ) { From ec942b6cf04f2934336da343592285c67f6ae548 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 23 Mar 2016 09:42:26 -0600 Subject: [PATCH 020/318] Bring javaScript client up to date * Add args and options on prototypes * Add `_.contains` alias for lodash support. --- wp-api.js | 273 ++++++++++++++++++++++++++++++++----------------- wp-api.min.js | 2 +- wp-api.min.map | 2 +- 3 files changed, 184 insertions(+), 93 deletions(-) mode change 100755 => 100644 wp-api.js diff --git a/wp-api.js b/wp-api.js old mode 100755 new mode 100644 index 7a0aa8de70..2ebb97cc97 --- a/wp-api.js +++ b/wp-api.js @@ -12,6 +12,11 @@ wp.api = wp.api || new WP_API(); wp.api.versionString = wp.api.versionString || 'wp/v2/'; + // Alias _includes to _.contains, ensuring it is available. + if ( ! _.isFunction( _.includes ) && _.isFunction( _.contains ) ) { + _.includes = _.contains; + } + })( window ); (function( window, undefined ) { @@ -152,49 +157,49 @@ }; /** - * Add defaults to a model from a route's endpoints. + * Add args and options to a model prototype from a route's endpoints. * * @param {array} routeEndpoints Array of route endpoints. * @param {Object} modelInstance An instance of the model (or collection) - * to add the defaults to. + * to add the args to. */ wp.api.utils.decorateFromRoute = function( routeEndpoints, modelInstance ) { /** - * Build the defaults based on route endpoint data. + * Build the args based on route endpoint data. */ _.each( routeEndpoints, function( routeEndpoint ) { - // Add post and edit endpoints as model defaults. - if ( _.contains( routeEndpoint.methods, 'POST' ) || _.contains( routeEndpoint.methods, 'PUT' ) ) { + // Add post and edit endpoints as model args. + if ( _.includes( routeEndpoint.methods, 'POST' ) || _.includes( routeEndpoint.methods, 'PUT' ) ) { - // Add any non empty args, merging them into the defaults object. + // Add any non empty args, merging them into the args object. if ( ! _.isEmpty( routeEndpoint.args ) ) { - // Set as defauls if no defaults yet. - if ( _.isEmpty( modelInstance.defaults ) ) { - modelInstance.defaults = routeEndpoint.args; + // Set as defauls if no args yet. + if ( _.isEmpty( modelInstance.prototype.args ) ) { + modelInstance.prototype.args = routeEndpoint.args; } else { - // We already have defaults, merge these new args in. - modelInstance.defaults = _.union( routeEndpoint.args, modelInstance.defaults ); + // We already have args, merge these new args in. + modelInstance.prototype.args = _.union( routeEndpoint.args, modelInstance.prototype.defaults ); } } } else { // Add GET method as model options. - if ( _.contains( routeEndpoint.methods, 'GET' ) ) { + if ( _.includes( routeEndpoint.methods, 'GET' ) ) { // Add any non empty args, merging them into the defaults object. if ( ! _.isEmpty( routeEndpoint.args ) ) { // Set as defauls if no defaults yet. - if ( _.isEmpty( modelInstance.options ) ) { - modelInstance.options = routeEndpoint.args; + if ( _.isEmpty( modelInstance.prototype.options ) ) { + modelInstance.prototype.options = routeEndpoint.args; } else { // We already have options, merge these new args in. - modelInstance.options = _.union( routeEndpoint.args, modelInstance.options ); + modelInstance.prototype.options = _.union( routeEndpoint.args, modelInstance.prototype.options ); } } @@ -333,12 +338,12 @@ /** * Build a helper to retrieve a collection. * - * @param {string} parentModel The parent model. - * @param {string} collectionName The name to use when constructing the collection. - * @param {string} embedSourcePoint Where to check the embedds object for _embed data. - * @param {string} embedIndex An addiitonal optional index for the _embed data. + * @param {string} parentModel The parent model. + * @param {string} collectionName The name to use when constructing the collection. + * @param {string} embedSourcePoint Where to check the embedds object for _embed data. + * @param {string} embedIndex An addiitonal optional index for the _embed data. * - * @return {Deferred.promise} A promise which resolves to the constructed collection. + * @return {Deferred.promise} A promise which resolves to the constructed collection. */ buildCollectionGetter = function( parentModel, collectionName, embedSourcePoint, embedIndex ) { /** @@ -439,32 +444,112 @@ * Add a helper funtion to handle post Tags. */ TagsMixin = { + + /** + * Get the tags for a post. + * + * @return {Deferred.promise} promise Resolves to an array of tags. + */ getTags: function() { - return buildCollectionGetter( this, 'PostTags', 'https://api.w.org/term', 1 ); + var tagIds = this.get( 'tags' ), + tags = new wp.api.collections.Tags(); + + // Resolve with an empty array if no tags. + if ( _.isEmpty( tagIds ) ) { + return jQuery.Deferred().resolve( [] ); + } + + return tags.fetch( { data: { include: tagIds } } ); + }, + + /** + * Set the tags for a post. + * + * Accepts an array of tag slugs, or a Tags collection. + * + * @param {array|Backbone.Collection} tags The tags to set on the post. + * + */ + setTags: function( tags ) { + var allTags, newTag, + self = this, + newTags = []; + + if ( _.isString( tags ) ) { + return false; + } + + // If this is an array of slugs, build a collection. + if ( _.isArray( tags ) ) { + + // Get all the tags. + allTags = new wp.api.collections.Tags(); + allTags.fetch( { + data: { per_page: 100 }, + success: function( alltags ) { + + // Find the passed tags and set them up. + _.each( tags, function( tag ) { + newTag = new wp.api.models.Tag( alltags.findWhere( { slug: tag } ) ); + + // Tie the new tag to the post. + newTag.set( 'parent_post', self.get( 'id' ) ); + + // Add the new tag to the collection. + newTags.push( newTag ); + } ); + tags = new wp.api.collections.Tags( newTags ); + self.setTagsWithCollection( tags ); + } + } ); + + } else { + this.setTagsWithCollection( tags ); + } + }, + + /** + * Set the tags for a post. + * + * Accepts a Tags collection. + * + * @param {array|Backbone.Collection} tags The tags to set on the post. + * + */ + setTagsWithCollection: function( tags ) { + + // Pluck out the category ids. + this.set( 'tags', tags.pluck( 'id' ) ); + return this.save(); } }, + /** * Add a helper funtion to handle post Categories. */ CategoriesMixin = { /** - * Get a PostCategories model for an model's categories. - * - * Uses the embedded data if available, otherwises fetches the - * data from the server. + * Get a the categories for a post. * - * @return {Deferred.promise} promise Resolves to a wp.api.collections.PostCategories - * collection containing the post categories. + * @return {Deferred.promise} promise Resolves to an array of categories. */ getCategories: function() { - return buildCollectionGetter( this, 'PostCategories', 'https://api.w.org/term', 0 ); + var categoryIds = this.get( 'categories' ), + categories = new wp.api.collections.Categories(); + + // Resolve with an empty array if no categories. + if ( _.isEmpty( categoryIds ) ) { + return jQuery.Deferred().resolve( [] ); + } + + return categories.fetch( { data: { include: categoryIds } } ); }, /** * Set the categories for a post. * - * Accepts an array of category slugs, or a PostCategories collection. + * Accepts an array of category slugs, or a Categories collection. * * @param {array|Backbone.Collection} categories The categories to set on the post. * @@ -474,17 +559,22 @@ self = this, newCategories = []; + if ( _.isString( categories ) ) { + return false; + } + // If this is an array of slugs, build a collection. if ( _.isArray( categories ) ) { // Get all the categories. allCategories = new wp.api.collections.Categories(); allCategories.fetch( { + data: { per_page: 100 }, success: function( allcats ) { // Find the passed categories and set them up. _.each( categories, function( category ) { - newCategory = new wp.api.models.PostCategories( allcats.findWhere( { slug: category } ) ); + newCategory = new wp.api.models.Category( allcats.findWhere( { slug: category } ) ); // Tie the new category to the post. newCategory.set( 'parent_post', self.get( 'id' ) ); @@ -492,7 +582,7 @@ // Add the new category to the collection. newCategories.push( newCategory ); } ); - categories = new wp.api.collections.PostCategories( newCategories ); + categories = new wp.api.collections.Categories( newCategories ); self.setCategoriesWithCollection( categories ); } } ); @@ -506,37 +596,16 @@ /** * Set the categories for a post. * - * Accepts PostCategories collection. + * Accepts Categories collection. * * @param {array|Backbone.Collection} categories The categories to set on the post. * */ setCategoriesWithCollection: function( categories ) { - var removedCategories, addedCategories, categoriesIds, existingCategoriesIds; - - // Get the existing categories. - this.getCategories().done( function( existingCategories ) { - - // Pluck out the category ids. - categoriesIds = categories.pluck( 'id' ); - existingCategoriesIds = existingCategories.pluck( 'id' ); - - // Calculate which categories have been removed or added (leave the rest). - addedCategories = _.difference( categoriesIds, existingCategoriesIds ); - removedCategories = _.difference( existingCategoriesIds, categoriesIds ); - - // Add the added categories. - _.each( addedCategories, function( addedCategory ) { - // Save the new categories on the post with a 'POST' method, not Backbone's default 'PUT'. - existingCategories.create( categories.get( addedCategory ), { type: 'POST' } ); - } ); - - // Remove the removed categories. - _.each( removedCategories, function( removedCategory ) { - existingCategories.get( removedCategory ).destroy(); - } ); - } ); + // Pluck out the category ids. + this.set( 'categories', categories.pluck( 'id' ) ); + return this.save(); } }, @@ -559,13 +628,13 @@ }; // Exit if we don't have valid model defaults. - if ( _.isUndefined( model.defaults ) ) { + if ( _.isUndefined( model.prototype.args ) ) { return model; } // Go thru the parsable date fields, if our model contains any of them it gets the TimeStampedMixin. _.each( parseableDates, function( theDateKey ) { - if ( ! _.isUndefined( model.defaults[ theDateKey ] ) ) { + if ( ! _.isUndefined( model.prototype.args[ theDateKey ] ) ) { hasDate = true; } } ); @@ -576,17 +645,17 @@ } // Add the AuthorMixin for models that contain an author. - if ( ! _.isUndefined( model.defaults.author ) ) { + if ( ! _.isUndefined( model.prototype.args.author ) ) { model = model.extend( AuthorMixin ); } // Add the FeaturedImageMixin for models that contain a featured_image. - if ( ! _.isUndefined( model.defaults.featured_image ) ) { + if ( ! _.isUndefined( model.prototype.args.featured_image ) ) { model = model.extend( FeaturedImageMixin ); } // Add the CategoriesMixin for models that support categories collections. - if ( ! _.isUndefined( loadingObjects.collections[ modelClassName + 'Categories' ] ) ) { + if ( ! _.isUndefined( model.prototype.args.categories ) ) { model = model.extend( CategoriesMixin ); } @@ -596,7 +665,7 @@ } // Add the TagsMixin for models that support tags collections. - if ( ! _.isUndefined( loadingObjects.collections[ modelClassName + 'Tags' ] ) ) { + if ( ! _.isUndefined( model.prototype.args.tags ) ) { model = model.extend( TagsMixin ); } @@ -614,10 +683,12 @@ // Suppress warning about parse function's unused "options" argument: /* jshint unused:false */ -(function( wp, wpApiSettings, Backbone, window, undefined ) { +(function() { 'use strict'; + var wpApiSettings = window.wpApiSettings || {}; + /** * Backbone base model for all models. */ @@ -637,6 +708,16 @@ options = options || {}; + // Remove date_gmt if null. + if ( _.isNull( model.get( 'date_gmt' ) ) ) { + model.unset( 'date_gmt' ); + } + + // Remove slug if empty. + if ( _.isEmpty( model.get( 'slug' ) ) ) { + model.unset( 'slug' ); + } + if ( ! _.isUndefined( wpApiSettings.nonce ) && ! _.isNull( wpApiSettings.nonce ) ) { beforeSend = options.beforeSend; @@ -652,7 +733,7 @@ }; } - // Add '?force=true' to delete method when required. + // Add '?force=true' to use delete method when required. if ( this.requireForceForDelete && 'delete' === method ) { model.url = model.url() + '?force=true'; } @@ -665,7 +746,7 @@ save: function( attrs, options ) { // Do we have the put method, then execute the save. - if ( _.contains( this.methods, 'PUT' ) || _.contains( this.methods, 'POST' ) ) { + if ( _.includes( this.methods, 'PUT' ) || _.includes( this.methods, 'POST' ) ) { // Proxy the call to the original save function. return Backbone.Model.prototype.save.call( this, attrs, options ); @@ -682,7 +763,7 @@ destroy: function( options ) { // Do we have the DELETE method, then execute the destroy. - if ( _.contains( this.methods, 'DELETE' ) ) { + if ( _.includes( this.methods, 'DELETE' ) ) { // Proxy the call to the original save function. return Backbone.Model.prototype.destroy.call( this, options ); @@ -723,13 +804,14 @@ } } ); -})( wp, wpApiSettings, Backbone, window ); +})(); -/* global wpApiSettings:false */ -(function( wp, wpApiSettings, Backbone, _, window, undefined ) { +( function() { 'use strict'; + var wpApiSettings = window.wpApiSettings || {}; + /** * Contains basic collection functionality such as pagination. */ @@ -800,8 +882,10 @@ success = options.success; options.success = function( data, textStatus, request ) { - self.state.totalPages = parseInt( request.getResponseHeader( 'x-wp-totalpages' ), 10 ); - self.state.totalObjects = parseInt( request.getResponseHeader( 'x-wp-total' ), 10 ); + if ( ! _.isUndefined( request ) ) { + self.state.totalPages = parseInt( request.getResponseHeader( 'x-wp-totalpages' ), 10 ); + self.state.totalObjects = parseInt( request.getResponseHeader( 'x-wp-total' ), 10 ); + } if ( null === self.state.currentPage ) { self.state.currentPage = 1; @@ -862,17 +946,20 @@ } ); -})( wp, wpApiSettings, Backbone, _, window ); +} )(); -/* global wpApiSettings */ -(function( window, undefined ) { +( function() { 'use strict'; - var Endpoint, initializedDeferreds = {}; - + var Endpoint, initializedDeferreds = {}, + wpApiSettings = window.wpApiSettings || {}; window.wp = window.wp || {}; - wp.api = wp.api || {}; + wp.api = wp.api || {}; + + if ( _.isEmpty( wpApiSettings ) ) { + wpApiSettings.root = window.location.origin + '/wp-json/'; + } Endpoint = Backbone.Model.extend({ defaults: { @@ -991,19 +1078,14 @@ index !== schemaRoot && index !== ( '/' + routeModel.get( 'versionString' ).slice( 0, -1 ) ) ) { - /** - * Single item models end with a regex/variable. - * - * @todo make model/collection logic more robust. - */ - if ( index.endsWith( '+)' ) ) { + + // Single items end with a regex (or the special case 'me'). + if ( /.*[+)|me]$/.test( index ) ) { modelRoutes.push( { index: index, route: route } ); } else { // Collections end in a name. - if ( ! index.endsWith( 'me' ) ) { - collectionRoutes.push( { index: index, route: route } ); - } + collectionRoutes.push( { index: index, route: route } ); } } } ); @@ -1018,7 +1100,13 @@ // Extract the name and any parent from the route. var modelClassName, routeName = wp.api.utils.extractRoutePart( modelRoute.index, 2 ), - parentName = wp.api.utils.extractRoutePart( modelRoute.index, 4 ); + parentName = wp.api.utils.extractRoutePart( modelRoute.index, 4 ), + routeEnd = wp.api.utils.extractRoutePart( modelRoute.index, 1 ); + + // Handle the special case of the 'me' route. + if ( 'me' === routeEnd ) { + routeName = 'me'; + } // If the model has a parent in its route, add that to its class name. if ( '' !== parentName && parentName !== routeName ) { @@ -1059,7 +1147,7 @@ if ( 'Posts' !== this.name && 'Pages' !== this.name && - _.contains( this.methods, 'DELETE' ) + _.includes( this.methods, 'DELETE' ) ) { this.requireForceForDelete = true; } @@ -1074,7 +1162,10 @@ // Function that returns a constructed url based on the id. url: function() { - var url = routeModel.get( 'apiRoot' ) + routeModel.get( 'versionString' ) + routeName; + var url = routeModel.get( 'apiRoot' ) + + routeModel.get( 'versionString' ) + + ( ( 'me' === routeName ) ? 'users/me' : routeName ); + if ( ! _.isUndefined( this.get( 'id' ) ) ) { url += '/' + this.get( 'id' ); } @@ -1223,6 +1314,6 @@ */ // The wp.api.init function returns a promise that will resolve with the endpoint once it is ready. - wp.api.init(); + wp.api.loadPromise = wp.api.init(); -})( window ); +} )(); diff --git a/wp-api.min.js b/wp-api.min.js index 6f95da773b..de28e22095 100644 --- a/wp-api.min.js +++ b/wp-api.min.js @@ -1,2 +1,2 @@ -!function(a,b){"use strict";function c(){this.models={},this.collections={},this.views={}}a.wp=a.wp||{},wp.api=wp.api||new c,wp.api.versionString=wp.api.versionString||"wp/v2/"}(window),function(a,b){"use strict";var c,d;a.wp=a.wp||{},wp.api=wp.api||{},wp.api.utils=wp.api.utils||{},Date.prototype.toISOString||(c=function(a){return d=String(a),1===d.length&&(d="0"+d),d},Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+c(this.getUTCMonth()+1)+"-"+c(this.getUTCDate())+"T"+c(this.getUTCHours())+":"+c(this.getUTCMinutes())+":"+c(this.getUTCSeconds())+"."+String((this.getUTCMilliseconds()/1e3).toFixed(3)).slice(2,5)+"Z"}),wp.api.utils.parseISO8601=function(a){var c,d,e,f,g=0,h=[1,4,5,6,7,10,11];if(d=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(a)){for(e=0;f=h[e];++e)d[f]=+d[f]||0;d[2]=(+d[2]||1)-1,d[3]=+d[3]||1,"Z"!==d[8]&&b!==d[9]&&(g=60*d[10]+d[11],"+"===d[9]&&(g=0-g)),c=Date.UTC(d[1],d[2],d[3],d[4],d[5]+g,d[6],d[7])}else c=Date.parse?Date.parse(a):NaN;return c},wp.api.utils.getRootUrl=function(){return a.location.origin?a.location.origin+"/":a.location.protocol+"/"+a.location.host+"/"},wp.api.utils.capitalize=function(a){return _.isUndefined(a)?a:a.charAt(0).toUpperCase()+a.slice(1)},wp.api.utils.extractRoutePart=function(a,b){var c;return b=b||1,a=a.replace(wp.api.versionString,""),c=a.split("/").reverse(),_.isUndefined(c[--b])?"":c[b]},wp.api.utils.extractParentName=function(a){var b,c=a.lastIndexOf("_id>[\\d]+)/");return 0>c?"":(b=a.substr(0,c-1),b=b.split("/"),b.pop(),b=b.pop())},wp.api.utils.decorateFromRoute=function(a,b){_.each(a,function(a){_.contains(a.methods,"POST")||_.contains(a.methods,"PUT")?_.isEmpty(a.args)||(_.isEmpty(b.defaults)?b.defaults=a.args:b.defaults=_.union(a.args,b.defaults)):_.contains(a.methods,"GET")&&(_.isEmpty(a.args)||(_.isEmpty(b.options)?b.options=a.args:b.options=_.union(a.args,b.options)))})},wp.api.utils.addMixinsAndHelpers=function(a,b,c){var d=!1,e=["date","modified","date_gmt","modified_gmt"],f={toJSON:function(){var a=_.clone(this.attributes);return _.each(e,function(b){b in a&&(_.isNull(a[b])||(a[b]=a[b].toISOString()))}),a},parse:function(a){var b;return _.each(e,function(c){c in a&&(_.isNull(a[c])||(b=wp.api.utils.parseISO8601(a[c]),a[c]=new Date(b)))}),a}},g=function(a,b,c,d,e){var f,g,h,i;return i=jQuery.Deferred(),g=a.get("_embedded")||{},_.isNumber(b)?(g[d]&&(h=_.findWhere(g[d],{id:b})),h||(h={id:b}),f=new wp.api.models[c](h),f.get(e)?i.resolve(f):f.fetch({success:function(a){i.resolve(a)}}),i.promise()):(i.reject(),i)},h=function(a,b,c,d){var e,f,g,h="",j="",k=jQuery.Deferred();return e=a.get("id"),f=a.get("_embedded")||{},_.isNumber(e)&&0!==e?(_.isUndefined(c)||_.isUndefined(f[c])?h={parent:e}:j=_.isUndefined(d)?f[c]:f[c][d],g=new wp.api.collections[b](j,h),_.isUndefined(g.models[0])?g.fetch({success:function(a){i(a,e),k.resolve(a)}}):(i(g,e),k.resolve(g)),k.promise()):(k.reject(),k)},i=function(a,b){_.each(a.models,function(a){a.set("parent_post",b)})},j={getMeta:function(){return h(this,"PostMeta","https://api.w.org/meta")}},k={getRevisions:function(){return h(this,"PostRevisions")}},l={getTags:function(){return h(this,"PostTags","https://api.w.org/term",1)}},m={getCategories:function(){return h(this,"PostCategories","https://api.w.org/term",0)},setCategories:function(a){var b,c,d=this,e=[];_.isArray(a)?(b=new wp.api.collections.Categories,b.fetch({success:function(b){_.each(a,function(a){c=new wp.api.models.PostCategories(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.PostCategories(e),d.setCategoriesWithCollection(a)}})):this.setCategoriesWithCollection(a)},setCategoriesWithCollection:function(a){var b,c,d,e;this.getCategories().done(function(f){d=a.pluck("id"),e=f.pluck("id"),c=_.difference(d,e),b=_.difference(e,d),_.each(c,function(b){f.create(a.get(b),{type:"POST"})}),_.each(b,function(a){f.get(a).destroy()})})}},n={getAuthorUser:function(){return g(this,this.get("author"),"User","author","name")}},o={getFeaturedImage:function(){return g(this,this.get("featured_image"),"Media","https://api.w.org/featuredmedia","source_url")}};return _.isUndefined(a.defaults)?a:(_.each(e,function(b){_.isUndefined(a.defaults[b])||(d=!0)}),d&&(a=a.extend(f)),_.isUndefined(a.defaults.author)||(a=a.extend(n)),_.isUndefined(a.defaults.featured_image)||(a=a.extend(o)),_.isUndefined(c.collections[b+"Categories"])||(a=a.extend(m)),_.isUndefined(c.collections[b+"Meta"])||(a=a.extend(j)),_.isUndefined(c.collections[b+"Tags"])||(a=a.extend(l)),_.isUndefined(c.collections[b+"Revisions"])||(a=a.extend(k)),a)}}(window),function(a,b,c,d,e){"use strict";a.api.WPApiBaseModel=c.Model.extend({sync:function(a,d,e){var f;return e=e||{},_.isUndefined(b.nonce)||_.isNull(b.nonce)||(f=e.beforeSend,e.beforeSend=function(a){return a.setRequestHeader("X-WP-Nonce",b.nonce),f?f.apply(this,arguments):void 0}),this.requireForceForDelete&&"delete"===a&&(d.url=d.url()+"?force=true"),c.sync(a,d,e)},save:function(a,b){return _.contains(this.methods,"PUT")||_.contains(this.methods,"POST")?c.Model.prototype.save.call(this,a,b):!1},destroy:function(a){return _.contains(this.methods,"DELETE")?c.Model.prototype.destroy.call(this,a):!1}}),a.api.models.Schema=a.api.WPApiBaseModel.extend({defaults:{_links:{},namespace:null,routes:{}},initialize:function(c,d){var e=this;d=d||{},a.api.WPApiBaseModel.prototype.initialize.call(e,c,d),e.apiRoot=d.apiRoot||b.root,e.versionString=d.versionString||b.versionString},url:function(){return this.apiRoot+this.versionString}})}(wp,wpApiSettings,Backbone,window),function(a,b,c,d,e,f){"use strict";a.api.WPApiBaseCollection=c.Collection.extend({initialize:function(a,b){this.state={data:{},currentPage:null,totalPages:null,totalObjects:null},d.isUndefined(b)?this.parent="":this.parent=b.parent},sync:function(a,e,f){var g,h,i=this;return f=f||{},g=f.beforeSend,"undefined"!=typeof b.nonce&&(f.beforeSend=function(a){return a.setRequestHeader("X-WP-Nonce",b.nonce),g?g.apply(i,arguments):void 0}),"read"===a&&(f.data?(i.state.data=d.clone(f.data),delete i.state.data.page):i.state.data=f.data={},"undefined"==typeof f.data.page?(i.state.currentPage=null,i.state.totalPages=null,i.state.totalObjects=null):i.state.currentPage=f.data.page-1,h=f.success,f.success=function(a,b,c){return i.state.totalPages=parseInt(c.getResponseHeader("x-wp-totalpages"),10),i.state.totalObjects=parseInt(c.getResponseHeader("x-wp-total"),10),null===i.state.currentPage?i.state.currentPage=1:i.state.currentPage++,h?h.apply(this,arguments):void 0}),c.sync(a,e,f)},more:function(a){if(a=a||{},a.data=a.data||{},d.extend(a.data,this.state.data),"undefined"==typeof a.data.page){if(!this.hasMore())return!1;null===this.state.currentPage||this.state.currentPage<=1?a.data.page=2:a.data.page=this.state.currentPage+1}return this.fetch(a)},hasMore:function(){return null===this.state.totalPages||null===this.state.totalObjects||null===this.state.currentPage?null:this.state.currentPage[\\d]+)/");return 0>c?"":(b=a.substr(0,c-1),b=b.split("/"),b.pop(),b=b.pop())},wp.api.utils.decorateFromRoute=function(a,b){_.each(a,function(a){_.includes(a.methods,"POST")||_.includes(a.methods,"PUT")?_.isEmpty(a.args)||(_.isEmpty(b.prototype.args)?b.prototype.args=a.args:b.prototype.args=_.union(a.args,b.prototype.defaults)):_.includes(a.methods,"GET")&&(_.isEmpty(a.args)||(_.isEmpty(b.prototype.options)?b.prototype.options=a.args:b.prototype.options=_.union(a.args,b.prototype.options)))})},wp.api.utils.addMixinsAndHelpers=function(a,b,c){var d=!1,e=["date","modified","date_gmt","modified_gmt"],f={toJSON:function(){var a=_.clone(this.attributes);return _.each(e,function(b){b in a&&(_.isNull(a[b])||(a[b]=a[b].toISOString()))}),a},parse:function(a){var b;return _.each(e,function(c){c in a&&(_.isNull(a[c])||(b=wp.api.utils.parseISO8601(a[c]),a[c]=new Date(b)))}),a}},g=function(a,b,c,d,e){var f,g,h,i;return i=jQuery.Deferred(),g=a.get("_embedded")||{},_.isNumber(b)?(g[d]&&(h=_.findWhere(g[d],{id:b})),h||(h={id:b}),f=new wp.api.models[c](h),f.get(e)?i.resolve(f):f.fetch({success:function(a){i.resolve(a)}}),i.promise()):(i.reject(),i)},h=function(a,b,c,d){var e,f,g,h="",j="",k=jQuery.Deferred();return e=a.get("id"),f=a.get("_embedded")||{},_.isNumber(e)&&0!==e?(_.isUndefined(c)||_.isUndefined(f[c])?h={parent:e}:j=_.isUndefined(d)?f[c]:f[c][d],g=new wp.api.collections[b](j,h),_.isUndefined(g.models[0])?g.fetch({success:function(a){i(a,e),k.resolve(a)}}):(i(g,e),k.resolve(g)),k.promise()):(k.reject(),k)},i=function(a,b){_.each(a.models,function(a){a.set("parent_post",b)})},j={getMeta:function(){return h(this,"PostMeta","https://api.w.org/meta")}},k={getRevisions:function(){return h(this,"PostRevisions")}},l={getTags:function(){var a=this.get("tags"),b=new wp.api.collections.Tags;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setTags:function(a){var b,c,d=this,e=[];return _.isString(a)?!1:void(_.isArray(a)?(b=new wp.api.collections.Tags,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Tag(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Tags(e),d.setTagsWithCollection(a)}})):this.setTagsWithCollection(a))},setTagsWithCollection:function(a){return this.set("tags",a.pluck("id")),this.save()}},m={getCategories:function(){var a=this.get("categories"),b=new wp.api.collections.Categories;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setCategories:function(a){var b,c,d=this,e=[];return _.isString(a)?!1:void(_.isArray(a)?(b=new wp.api.collections.Categories,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Category(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Categories(e),d.setCategoriesWithCollection(a)}})):this.setCategoriesWithCollection(a))},setCategoriesWithCollection:function(a){return this.set("categories",a.pluck("id")),this.save()}},n={getAuthorUser:function(){return g(this,this.get("author"),"User","author","name")}},o={getFeaturedImage:function(){return g(this,this.get("featured_image"),"Media","https://api.w.org/featuredmedia","source_url")}};return _.isUndefined(a.prototype.args)?a:(_.each(e,function(b){_.isUndefined(a.prototype.args[b])||(d=!0)}),d&&(a=a.extend(f)),_.isUndefined(a.prototype.args.author)||(a=a.extend(n)),_.isUndefined(a.prototype.args.featured_image)||(a=a.extend(o)),_.isUndefined(a.prototype.args.categories)||(a=a.extend(m)),_.isUndefined(c.collections[b+"Meta"])||(a=a.extend(j)),_.isUndefined(a.prototype.args.tags)||(a=a.extend(l)),_.isUndefined(c.collections[b+"Revisions"])||(a=a.extend(k)),a)}}(window),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseModel=Backbone.Model.extend({sync:function(b,c,d){var e;return d=d||{},_.isNull(c.get("date_gmt"))&&c.unset("date_gmt"),_.isEmpty(c.get("slug"))&&c.unset("slug"),_.isUndefined(a.nonce)||_.isNull(a.nonce)||(e=d.beforeSend,d.beforeSend=function(b){return b.setRequestHeader("X-WP-Nonce",a.nonce),e?e.apply(this,arguments):void 0}),this.requireForceForDelete&&"delete"===b&&(c.url=c.url()+"?force=true"),Backbone.sync(b,c,d)},save:function(a,b){return _.includes(this.methods,"PUT")||_.includes(this.methods,"POST")?Backbone.Model.prototype.save.call(this,a,b):!1},destroy:function(a){return _.includes(this.methods,"DELETE")?Backbone.Model.prototype.destroy.call(this,a):!1}}),wp.api.models.Schema=wp.api.WPApiBaseModel.extend({defaults:{_links:{},namespace:null,routes:{}},initialize:function(b,c){var d=this;c=c||{},wp.api.WPApiBaseModel.prototype.initialize.call(d,b,c),d.apiRoot=c.apiRoot||a.root,d.versionString=c.versionString||a.versionString},url:function(){return this.apiRoot+this.versionString}})}(),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseCollection=Backbone.Collection.extend({initialize:function(a,b){this.state={data:{},currentPage:null,totalPages:null,totalObjects:null},_.isUndefined(b)?this.parent="":this.parent=b.parent},sync:function(b,c,d){var e,f,g=this;return d=d||{},e=d.beforeSend,"undefined"!=typeof a.nonce&&(d.beforeSend=function(b){return b.setRequestHeader("X-WP-Nonce",a.nonce),e?e.apply(g,arguments):void 0}),"read"===b&&(d.data?(g.state.data=_.clone(d.data),delete g.state.data.page):g.state.data=d.data={},"undefined"==typeof d.data.page?(g.state.currentPage=null,g.state.totalPages=null,g.state.totalObjects=null):g.state.currentPage=d.data.page-1,f=d.success,d.success=function(a,b,c){return _.isUndefined(c)||(g.state.totalPages=parseInt(c.getResponseHeader("x-wp-totalpages"),10),g.state.totalObjects=parseInt(c.getResponseHeader("x-wp-total"),10)),null===g.state.currentPage?g.state.currentPage=1:g.state.currentPage++,f?f.apply(this,arguments):void 0}),Backbone.sync(b,c,d)},more:function(a){if(a=a||{},a.data=a.data||{},_.extend(a.data,this.state.data),"undefined"==typeof a.data.page){if(!this.hasMore())return!1;null===this.state.currentPage||this.state.currentPage<=1?a.data.page=2:a.data.page=this.state.currentPage+1}return this.fetch(a)},hasMore:function(){return null===this.state.totalPages||null===this.state.totalObjects||null===this.state.currentPage?null:this.state.currentPage Date: Wed, 23 Mar 2016 11:31:21 -0600 Subject: [PATCH 021/318] Bump script version --- extras.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extras.php b/extras.php index 2318c34a39..9cb7461a76 100755 --- a/extras.php +++ b/extras.php @@ -24,7 +24,7 @@ function rest_register_scripts() { // Use minified scripts if SCRIPT_DEBUG is not on. $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; - wp_register_script( 'wp-api', plugins_url( 'wp-api' . $suffix . '.js', __FILE__ ), array( 'jquery', 'backbone', 'underscore' ), '1.1', true ); + wp_register_script( 'wp-api', plugins_url( 'wp-api' . $suffix . '.js', __FILE__ ), array( 'jquery', 'backbone', 'underscore' ), '1.2', true ); $settings = array( 'root' => esc_url_raw( get_rest_url() ), From a76610a6bcb1a48889aefac906705a6c3033ce0c Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Mon, 28 Mar 2016 19:23:23 -0400 Subject: [PATCH 022/318] Use Compact links for embedded responses if they are available. --- lib/endpoints/class-wp-rest-controller.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 096abd92cc..4d9fd7a527 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -157,7 +157,14 @@ public function prepare_response_for_collection( $response ) { } $data = (array) $response->get_data(); - $links = WP_REST_Server::get_response_links( $response ); + $server = rest_get_server(); + + if ( method_exists( $server, 'get_compact_response_links' ) ) { + $links = call_user_func( array( $server, 'get_compact_response_links' ), $response ); + } else { + $links = call_user_func( array( $server, 'get_response_links' ), $response ); + } + if ( ! empty( $links ) ) { $data['_links'] = $links; } From 0614e083216214ba1337ba216750c4bd75b9b6f6 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Mon, 28 Mar 2016 19:30:02 -0400 Subject: [PATCH 023/318] Backport rest_get_server --- core-integration.php | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/core-integration.php b/core-integration.php index 38b261ebed..1cd9ebdbd2 100644 --- a/core-integration.php +++ b/core-integration.php @@ -35,3 +35,50 @@ function wp_parse_slug_list( $list ) { return array_unique( $list ); } } + +if ( ! function_exists( 'rest_get_server' ) ) { + /** + * Retrieves the current REST server instance. + * + * Instantiates a new instance if none exists already. + * + * @since 4.5.0 + * + * @global WP_REST_Server $wp_rest_server REST server instance. + * + * @return WP_REST_Server REST server instance. + */ + function rest_get_server() { + /* @var WP_REST_Server $wp_rest_server */ + global $wp_rest_server; + + if ( empty( $wp_rest_server ) ) { + /** + * Filter the REST Server Class. + * + * This filter allows you to adjust the server class used by the API, using a + * different class to handle requests. + * + * @since 4.4.0 + * + * @param string $class_name The name of the server class. Default 'WP_REST_Server'. + */ + $wp_rest_server_class = apply_filters( 'wp_rest_server_class', 'WP_REST_Server' ); + $wp_rest_server = new $wp_rest_server_class; + + /** + * Fires when preparing to serve an API request. + * + * Endpoint objects should be created and register their hooks on this action rather + * than another action to ensure they're only loaded when needed. + * + * @since 4.4.0 + * + * @param WP_REST_Server $wp_rest_server Server object. + */ + do_action( 'rest_api_init', $wp_rest_server ); + } + + return $wp_rest_server; + } +} From eceb87381838c2b195f3b72518cdb6fd2a29bd06 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Mon, 28 Mar 2016 19:34:57 -0400 Subject: [PATCH 024/318] Fix whitespace --- tests/test-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 3a8ab03c56..4327e937e4 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -1101,7 +1101,7 @@ protected function check_user_data( $user, $data, $context, $links ) { 'self', 'collection', ), array_keys( $links ) ); - + $this->assertArrayNotHasKey( 'password', $data ); } From 4d6977e77cb14f7272411016cc1145446b38d316 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Mon, 28 Mar 2016 16:36:40 -0700 Subject: [PATCH 025/318] First pass at changelog updates for Beta 13 --- CHANGELOG.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e816055e1a..8f28aae926 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,81 @@ # Changelog +## 2.0 Beta 13.0 (??? ??, 2016) + +- BREAKING CHANGE: Fix Content-Disposition header parsing. + + Allows regular form submissions from HTML forms, as well as properly formatted HTTP requests from clients. Note: this breaks backwards compatibility, as previously, the header parsing was completely wrong. + + (props @rmccue, [#2239](https://github.com/WP-API/WP-API/pull/2239)) + +- Only adds alternate link header for publicly viewable CPTs. + + (props @bradyvercher, [#2387](https://github.com/WP-API/WP-API/pull/2387)) + +- Adds `roles` param for `GET /wp/v2/users`. + + (props @BE-Webdesign, [#2372](https://github.com/WP-API/WP-API/pull/2372)) + +- Declares `password` in user schema, but never displays it. + + (props @danielbachhuber, [#2386](https://github.com/WP-API/WP-API/pull/2386)) + +- Permits `edit` context for requests which can edit the user. + + (props @danielbachhuber, [#2383](https://github.com/WP-API/WP-API/pull/2383)) + +- Adds `rest_pre_insert_{$taxonomy}` filter for terms. + + (props @kjbenk, [#2377](https://github.com/WP-API/WP-API/pull/2377)) + +- Supports taxonomy collection args on posts endpoint. + + (props @joehoyle, [#2287](https://github.com/WP-API/WP-API/pull/2287)) + +- Removes post meta link from post response. + + (props @joehoyle, [#2288](https://github.com/WP-API/WP-API/pull/2288)) + +- Registers `description` attribute when registering args from schema. + + (props @danielbachhuber, [#2362](https://github.com/WP-API/WP-API/pull/2362)) + +- Uses `$comment` from the database with `rest_insert_comment` action. + + (props @danielbachhuber, [#2349](https://github.com/WP-API/WP-API/pull/2349)) + +- Removes unnecessary global variables from users controller. + + (props @claudiosmweb, [#2335](https://github.com/WP-API/WP-API/pull/2335)) + +- Ensures `GET /wp/v2/categories` with out of bounds offset doesn't return results. + + (props @danielbachhuber, [#2313](https://github.com/WP-API/WP-API/pull/2313)) + +- Adds top-level support for date queries on posts and comments. + + (props @BE-Webdesign, [#2266](https://github.com/WP-API/WP-API/pull/2266), [#2291](https://github.com/WP-API/WP-API/pull/2291)) + +- Respects `show_avatars` setting for comments. + + (props @BE-Webdesign, [#2271](https://github.com/WP-API/WP-API/pull/2271)) + +- Uses cached `get_the_terms()` for terms-for-post for better performance. + + (props @rmccue, [#2257](https://github.com/WP-API/WP-API/pull/2257)) + +- Ensures comments search is an empty string. + + (props @rmccue, [#2256](https://github.com/WP-API/WP-API/pull/2256)) + +- If no title is provided in create attachment request or file metadata, falls back to filename. + + (props @danielbachhuber, [#2254](https://github.com/WP-API/WP-API/pull/2254)) + +- Removes unused `$img_url_basename` variable in attachments controller. + + (props @danielbachhuber, [#2250](https://github.com/WP-API/WP-API/pull/2250)) + ## 2.0 Beta 12.0 (February 9, 2016) - BREAKING CHANGE: Removes meta endpoints from primary plugin. From 286b48d13a91af3e691b09d4bafe36f5cb93bb70 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Mon, 28 Mar 2016 16:38:40 -0700 Subject: [PATCH 026/318] Mention JS client updates --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f28aae926..08cd7ac9d2 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ (props @rmccue, [#2239](https://github.com/WP-API/WP-API/pull/2239)) +- JavaScript client updates: + + * Support lodash, plus older and newer underscore: add an alias for `_.contains` + * Add args and options on the model/collection prototypes + * Rework category/tag mixins to support new API structure + * Add workaround for the null/empty values returned by the API when creating a new post - these values are not accepted for subsequent updates/saves, so explicitly excluding them. See https://github.com/WP-API/WP-API/pull/2393 + * Better handling of the (special) `me` endpoint + * Schema parsing cleanup + * Introduce `wp.api.loadPromise` so developers can ensure api load complete before using + + (props @adamsilverstein, [#2403](https://github.com/WP-API/WP-API/pull/2403)) + - Only adds alternate link header for publicly viewable CPTs. (props @bradyvercher, [#2387](https://github.com/WP-API/WP-API/pull/2387)) From 063ef518707d01beb8607abf1b9245e13fcc4b17 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Mon, 28 Mar 2016 16:48:53 -0700 Subject: [PATCH 027/318] Mention CONTRIBUTING.md in the README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 9a3074c417..d84a76971f 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,10 @@ Also, be sure to use the Subversion `trunk` branch of WordPress Core as there ar All tickets for the project are being tracked on [GitHub][]. You can also take a look at the [recent updates][] for the project. +## Contributing + +Want to get involved? Check out [Contributing.md][contributing] for details on submitting fixes and new features. + ## Security We take the security of the API extremely seriously. If you think you've found @@ -79,5 +83,6 @@ WordPress on your own server. **Do not test on servers you do not own.**) [docs]: http://v2.wp-api.org/ [GitHub]: https://github.com/WP-API/WP-API/issues +[contributing]: CONTRIBUTING.md [recent updates]: https://make.wordpress.org/core/tag/json-api/ [hackerone]: https://hackerone.com/wp-api From 44355ddbf983d0994332ead351ed54a63f92aec8 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Mon, 28 Mar 2016 17:06:20 -0700 Subject: [PATCH 028/318] Mention submitting a PR and setting up tests for local dev --- CONTRIBUTING.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a90bcb701..37b3d16d3e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,41 @@ Hi, and thanks for considering contributing! Before you do though, here's a few notes on how best to contribute. Don't worry, I'll keep it short! +## Getting Started + +### Submitting a pull request + +Contributions to WP-API always follow a feature branch pull request workflow. + +First, create a new branch for your changes. As far as branch naming goes, a +good rule of thumb is to use the issue number followed by a short slug that +represents the feature (e.g. `2392-contributing-docs`). + +Then, submit a pull request early to get a round of feedback on your proposed +changes. It's better to get quick feedback to ensure you're on the right track. + +Next, make sure you have adequate test coverage around your changes. With a +project of this magnitude, it's better to have too much test coverage than not +enough. + +Last, leave a #reviewmerge comment on your pull request for a final review by +the WP-API team. + +### Running tests + +The WP-API project uses two continuous integration (CI) services, Travis and +Scrutinizer, to automatically run a series of tests against its codebase on +every push. If you're submitting a pull request, it's expected that all tests +will pass — and that you add more tests for your change. + +You can install the necessary libraries in your local environment with: + + npm install -g grunt-cli + npm install + composer install + +Then, run PHPUnit tests with `phpunit`, or WPCS tests with `grunt phpcs`. + ## Best Practices ### Commit Messages From e17b5260c0ba27fbccbdfe46088e5f305b465014 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Tue, 29 Mar 2016 16:53:50 -0700 Subject: [PATCH 029/318] Update changelog with curies mention --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08cd7ac9d2..573cc78503 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ (props @rmccue, [#2239](https://github.com/WP-API/WP-API/pull/2239)) +- BREAKING CHANGE: Use compact links for embedded responses if they are available. + + Introduces curies for sites running WordPress 4.5 or greater; no changes for those running WordPress 4.4. + + (props @joehoyle, [#2412](https://github.com/WP-API/WP-API/pull/2412)) + - JavaScript client updates: * Support lodash, plus older and newer underscore: add an alias for `_.contains` From 3919693719b12232af4715e58a6bca6e77d293b1 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Tue, 29 Mar 2016 17:00:36 -0700 Subject: [PATCH 030/318] Version bump --- CHANGELOG.md | 2 +- bin/readme.txt | 98 ++++++++++++++++++++++++++++++++++++++++++++++++-- plugin.php | 2 +- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 573cc78503..7acee4a019 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2.0 Beta 13.0 (??? ??, 2016) +## 2.0 Beta 13.0 (March 29, 2016) - BREAKING CHANGE: Fix Content-Disposition header parsing. diff --git a/bin/readme.txt b/bin/readme.txt index b82f37c996..8eecf356be 100644 --- a/bin/readme.txt +++ b/bin/readme.txt @@ -2,8 +2,8 @@ Contributors: rmccue, rachelbaker, danielbachhuber, joehoyle Tags: json, rest, api, rest-api Requires at least: 4.4 -Tested up to: 4.5-alpha -Stable tag: 2.0-beta12 +Tested up to: 4.5 +Stable tag: 2.0-beta13 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -38,6 +38,100 @@ Once you've installed and activated the plugin, [check out the documentation](ht == Changelog == += 2.0 Beta 13.0 (March 29, 2016) = + +* BREAKING CHANGE: Fix Content-Disposition header parsing. + + Allows regular form submissions from HTML forms, as well as properly formatted HTTP requests from clients. Note: this breaks backwards compatibility, as previously, the header parsing was completely wrong. + + (props @rmccue, [#2239](https://github.com/WP-API/WP-API/pull/2239)) + +* BREAKING CHANGE: Use compact links for embedded responses if they are available. + + Introduces curies for sites running WordPress 4.5 or greater; no changes for those running WordPress 4.4. + + (props @joehoyle, [#2412](https://github.com/WP-API/WP-API/pull/2412)) + +* JavaScript client updates: + + * Support lodash, plus older and newer underscore: add an alias for `_.contains` + * Add args and options on the model/collection prototypes + * Rework category/tag mixins to support new API structure + * Add workaround for the null/empty values returned by the API when creating a new post * these values are not accepted for subsequent updates/saves, so explicitly excluding them. See https://github.com/WP-API/WP-API/pull/2393 + * Better handling of the (special) `me` endpoint + * Schema parsing cleanup + * Introduce `wp.api.loadPromise` so developers can ensure api load complete before using + + (props @adamsilverstein, [#2403](https://github.com/WP-API/WP-API/pull/2403)) + +* Only adds alternate link header for publicly viewable CPTs. + + (props @bradyvercher, [#2387](https://github.com/WP-API/WP-API/pull/2387)) + +* Adds `roles` param for `GET /wp/v2/users`. + + (props @BE-Webdesign, [#2372](https://github.com/WP-API/WP-API/pull/2372)) + +* Declares `password` in user schema, but never displays it. + + (props @danielbachhuber, [#2386](https://github.com/WP-API/WP-API/pull/2386)) + +* Permits `edit` context for requests which can edit the user. + + (props @danielbachhuber, [#2383](https://github.com/WP-API/WP-API/pull/2383)) + +* Adds `rest_pre_insert_{$taxonomy}` filter for terms. + + (props @kjbenk, [#2377](https://github.com/WP-API/WP-API/pull/2377)) + +* Supports taxonomy collection args on posts endpoint. + + (props @joehoyle, [#2287](https://github.com/WP-API/WP-API/pull/2287)) + +* Removes post meta link from post response. + + (props @joehoyle, [#2288](https://github.com/WP-API/WP-API/pull/2288)) + +* Registers `description` attribute when registering args from schema. + + (props @danielbachhuber, [#2362](https://github.com/WP-API/WP-API/pull/2362)) + +* Uses `$comment` from the database with `rest_insert_comment` action. + + (props @danielbachhuber, [#2349](https://github.com/WP-API/WP-API/pull/2349)) + +* Removes unnecessary global variables from users controller. + + (props @claudiosmweb, [#2335](https://github.com/WP-API/WP-API/pull/2335)) + +* Ensures `GET /wp/v2/categories` with out of bounds offset doesn't return results. + + (props @danielbachhuber, [#2313](https://github.com/WP-API/WP-API/pull/2313)) + +* Adds top-level support for date queries on posts and comments. + + (props @BE-Webdesign, [#2266](https://github.com/WP-API/WP-API/pull/2266), [#2291](https://github.com/WP-API/WP-API/pull/2291)) + +* Respects `show_avatars` setting for comments. + + (props @BE-Webdesign, [#2271](https://github.com/WP-API/WP-API/pull/2271)) + +* Uses cached `get_the_terms()` for terms-for-post for better performance. + + (props @rmccue, [#2257](https://github.com/WP-API/WP-API/pull/2257)) + +* Ensures comments search is an empty string. + + (props @rmccue, [#2256](https://github.com/WP-API/WP-API/pull/2256)) + +* If no title is provided in create attachment request or file metadata, falls back to filename. + + (props @danielbachhuber, [#2254](https://github.com/WP-API/WP-API/pull/2254)) + +* Removes unused `$img_url_basename` variable in attachments controller. + + (props @danielbachhuber, [#2250](https://github.com/WP-API/WP-API/pull/2250)) + = 2.0 Beta 12.0 (February 9, 2016) = * BREAKING CHANGE: Removes meta endpoints from primary plugin. diff --git a/plugin.php b/plugin.php index d8f5ea8913..830d972447 100755 --- a/plugin.php +++ b/plugin.php @@ -4,7 +4,7 @@ * Description: JSON-based REST API for WordPress, originally developed as part of GSoC 2013. * Author: WP REST API Team * Author URI: http://v2.wp-api.org - * Version: 2.0-beta12 + * Version: 2.0-beta13 * Plugin URI: https://github.com/WP-API/WP-API * License: GPL2+ */ From d0a392c3397960f81cf0bff0db239814c12aa228 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Tue, 29 Mar 2016 17:04:19 -0700 Subject: [PATCH 031/318] Proper file permissions --- CONTRIBUTING.md | 0 README.md | 0 wp-api.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 CONTRIBUTING.md mode change 100644 => 100755 README.md mode change 100644 => 100755 wp-api.js diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/wp-api.js b/wp-api.js old mode 100644 new mode 100755 From bdc8c4ae854f35a5223edd8bbc4d7e30c57ac8ef Mon Sep 17 00:00:00 2001 From: Nick Silva Date: Tue, 29 Mar 2016 17:36:51 -0700 Subject: [PATCH 032/318] Update return codes to 404 when object not found --- lib/endpoints/class-wp-rest-posts-controller.php | 6 +++--- lib/endpoints/class-wp-rest-terms-controller.php | 4 ++-- lib/endpoints/class-wp-rest-users-controller.php | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 469d763f0c..9adefeb18b 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -395,7 +395,7 @@ public function update_item( $request ) { $post = get_post( $id ); if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { - return new WP_Error( 'rest_post_invalid_id', __( 'Post id is invalid.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_post_invalid_id', __( 'Post id is invalid.' ), array( 'status' => 404 ) ); } $post = $this->prepare_item_for_database( $request ); @@ -795,7 +795,7 @@ protected function prepare_item_for_database( $request ) { if ( get_current_user_id() !== $post_author ) { $user_obj = get_userdata( $post_author ); if ( ! $user_obj ) { - return new WP_Error( 'rest_invalid_author', __( 'Invalid author id.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_invalid_author', __( 'Invalid author id.' ), array( 'status' => 404 ) ); } } $prepared_post->post_author = $post_author; @@ -906,7 +906,7 @@ protected function handle_featured_media( $featured_media, $post_id ) { if ( $result ) { return true; } else { - return new WP_Error( 'rest_invalid_featured_media', __( 'Invalid featured media id.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_invalid_featured_media', __( 'Invalid featured media id.' ), array( 'status' => 404 ) ); } } else { return delete_post_thumbnail( $post_id ); diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 5f525e59a2..3e87505978 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -340,7 +340,7 @@ public function create_item( $request ) { $parent = get_term( (int) $request['parent'], $this->taxonomy ); if ( ! $parent ) { - return new WP_Error( 'rest_term_invalid', __( "Parent resource doesn't exist." ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_term_invalid', __( "Parent resource doesn't exist." ), array( 'status' => 404 ) ); } } @@ -420,7 +420,7 @@ public function update_item( $request ) { $parent = get_term( (int) $request['parent'], $this->taxonomy ); if ( ! $parent ) { - return new WP_Error( 'rest_term_invalid', __( "Parent resource doesn't exist." ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_term_invalid', __( "Parent resource doesn't exist." ), array( 'status' => 404 ) ); } } diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 5b929d60f7..99a350079b 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -368,11 +368,11 @@ public function update_item( $request ) { $user = get_userdata( $id ); if ( ! $user ) { - return new WP_Error( 'rest_user_invalid_id', __( 'Invalid resource id.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_user_invalid_id', __( 'Invalid resource id.' ), array( 'status' => 404 ) ); } if ( email_exists( $request['email'] ) && $request['email'] !== $user->user_email ) { - return new WP_Error( 'rest_user_invalid_email', __( 'Email address is invalid.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_user_invalid_email', __( 'Email address is invalid.' ), array( 'status' => 404 ) ); } if ( ! empty( $request['username'] ) && $request['username'] !== $user->user_login ) { @@ -380,7 +380,7 @@ public function update_item( $request ) { } if ( ! empty( $request['slug'] ) && $request['slug'] !== $user->user_nicename && get_user_by( 'slug', $request['slug'] ) ) { - return new WP_Error( 'rest_user_invalid_slug', __( 'Slug is invalid.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_user_invalid_slug', __( 'Slug is invalid.' ), array( 'status' => 404 ) ); } if ( ! empty( $request['roles'] ) ) { @@ -451,7 +451,7 @@ public function delete_item( $request ) { $user = get_userdata( $id ); if ( ! $user ) { - return new WP_Error( 'rest_user_invalid_id', __( 'Invalid resource id.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_user_invalid_id', __( 'Invalid resource id.' ), array( 'status' => 404 ) ); } if ( ! empty( $reassign ) ) { @@ -628,7 +628,7 @@ protected function check_role_update( $user_id, $roles ) { foreach ( $roles as $role ) { if ( ! isset( $wp_roles->role_objects[ $role ] ) ) { - return new WP_Error( 'rest_user_invalid_role', sprintf( __( 'The role %s does not exist.' ), $role ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_user_invalid_role', sprintf( __( 'The role %s does not exist.' ), $role ), array( 'status' => 404 ) ); } $potential_role = $wp_roles->role_objects[ $role ]; From 4322c8a50d5ad18b4b12c3f0dfb2f245d5514bf0 Mon Sep 17 00:00:00 2001 From: Nick Silva Date: Wed, 30 Mar 2016 12:59:53 -0700 Subject: [PATCH 033/318] Revert status to 400 where incorrect or missing parameters --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- lib/endpoints/class-wp-rest-terms-controller.php | 4 ++-- lib/endpoints/class-wp-rest-users-controller.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 9adefeb18b..0a4378c7ad 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -795,7 +795,7 @@ protected function prepare_item_for_database( $request ) { if ( get_current_user_id() !== $post_author ) { $user_obj = get_userdata( $post_author ); if ( ! $user_obj ) { - return new WP_Error( 'rest_invalid_author', __( 'Invalid author id.' ), array( 'status' => 404 ) ); + return new WP_Error( 'rest_invalid_author', __( 'Invalid author id.' ), array( 'status' => 400 ) ); } } $prepared_post->post_author = $post_author; diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 3e87505978..5f525e59a2 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -340,7 +340,7 @@ public function create_item( $request ) { $parent = get_term( (int) $request['parent'], $this->taxonomy ); if ( ! $parent ) { - return new WP_Error( 'rest_term_invalid', __( "Parent resource doesn't exist." ), array( 'status' => 404 ) ); + return new WP_Error( 'rest_term_invalid', __( "Parent resource doesn't exist." ), array( 'status' => 400 ) ); } } @@ -420,7 +420,7 @@ public function update_item( $request ) { $parent = get_term( (int) $request['parent'], $this->taxonomy ); if ( ! $parent ) { - return new WP_Error( 'rest_term_invalid', __( "Parent resource doesn't exist." ), array( 'status' => 404 ) ); + return new WP_Error( 'rest_term_invalid', __( "Parent resource doesn't exist." ), array( 'status' => 400 ) ); } } diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 99a350079b..e0012938b5 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -372,7 +372,7 @@ public function update_item( $request ) { } if ( email_exists( $request['email'] ) && $request['email'] !== $user->user_email ) { - return new WP_Error( 'rest_user_invalid_email', __( 'Email address is invalid.' ), array( 'status' => 404 ) ); + return new WP_Error( 'rest_user_invalid_email', __( 'Email address is invalid.' ), array( 'status' => 400 ) ); } if ( ! empty( $request['username'] ) && $request['username'] !== $user->user_login ) { @@ -628,7 +628,7 @@ protected function check_role_update( $user_id, $roles ) { foreach ( $roles as $role ) { if ( ! isset( $wp_roles->role_objects[ $role ] ) ) { - return new WP_Error( 'rest_user_invalid_role', sprintf( __( 'The role %s does not exist.' ), $role ), array( 'status' => 404 ) ); + return new WP_Error( 'rest_user_invalid_role', sprintf( __( 'The role %s does not exist.' ), $role ), array( 'status' => 400 ) ); } $potential_role = $wp_roles->role_objects[ $role ]; From e4d8fd98df1f02b2029d9ddc55596a312f306b83 Mon Sep 17 00:00:00 2001 From: Nick Silva Date: Wed, 30 Mar 2016 13:00:16 -0700 Subject: [PATCH 034/318] Updated status codes for test cases --- tests/test-rest-posts-controller.php | 4 ++-- tests/test-rest-users-controller.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index f5bf647c1f..1dd5a3091c 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -1241,7 +1241,7 @@ public function test_update_post_invalid_id() { $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', REST_TESTS_IMPOSSIBLY_HIGH_NUMBER ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_post_invalid_id', $response, 400 ); + $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 ); } public function test_update_post_invalid_route() { @@ -1250,7 +1250,7 @@ public function test_update_post_invalid_route() { $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/pages/%d', $this->post_id ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_post_invalid_id', $response, 400 ); + $this->assertErrorResponse( 'rest_post_invalid_id', $response, 404 ); } public function test_update_post_with_format() { diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 4327e937e4..0956ae8f5c 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -857,7 +857,7 @@ public function test_update_user_invalid_id() { $request->set_body_params( $params ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_user_invalid_id', $response, 400 ); + $this->assertErrorResponse( 'rest_user_invalid_id', $response, 404 ); } public function test_delete_item() { @@ -913,7 +913,7 @@ public function test_delete_user_invalid_id() { $request['force'] = true; $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_user_invalid_id', $response, 400 ); + $this->assertErrorResponse( 'rest_user_invalid_id', $response, 404 ); } public function test_delete_user_reassign() { From 8865d5784a88c4472a20f497a072d9b8c07e245b Mon Sep 17 00:00:00 2001 From: Websupporter Date: Wed, 6 Apr 2016 21:57:41 +0300 Subject: [PATCH 035/318] Only include Post data on the response object when declared in schema #2416 --- lib/endpoints/class-wp-rest-posts-controller.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 469d763f0c..0ad7e60ce9 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1175,6 +1175,11 @@ public function prepare_item_for_response( $post, $request ) { $data[ $base ] = $terms ? wp_list_pluck( $terms, 'term_id' ) : array(); } + //Remove all data, which is not part of the scheme + foreach( $data as $key => $property ) + if( ! isset( $schema['properties'][ $key ] ) || empty( $schema['properties'][ $key ] ) ) + unset( $data[ $key ] ); + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); From 5f351d6c45600fcc8f3123df84fd62d30c90f3dd Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Thu, 7 Apr 2016 19:47:06 -0400 Subject: [PATCH 036/318] #2424-mixed-usage-of-rest-url Should fix #2424, however, after writing a unit test it doesn't appear to matter whether the '/' is there or not as get_rest_url adds a leading slash regardless as far as I can tell. --- .../class-wp-rest-attachments-controller.php | 2 +- .../class-wp-rest-comments-controller.php | 14 +++++++------- .../class-wp-rest-post-statuses-controller.php | 4 ++-- lib/endpoints/class-wp-rest-posts-controller.php | 12 ++++++------ .../class-wp-rest-revisions-controller.php | 2 +- lib/endpoints/class-wp-rest-terms-controller.php | 6 +++--- lib/endpoints/class-wp-rest-users-controller.php | 10 +++++----- tests/class-wp-test-rest-controller-testcase.php | 6 +++++- tests/test-rest-categories-controller.php | 12 ++++++------ tests/test-rest-tags-controller.php | 10 +++++----- tests/test-rest-users-controller.php | 14 +++++++------- 11 files changed, 48 insertions(+), 44 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index 38c86ba60f..f948e9e132 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -140,7 +140,7 @@ public function create_item( $request ) { $response = $this->prepare_item_for_response( $attachment, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); - $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) ); + $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) ); /** * Fires after a single attachment is created or updated via the REST API. diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index b0932ca8e9..72e70b0d0b 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -193,7 +193,7 @@ public function get_items( $request ) { $response->header( 'X-WP-Total', $total_comments ); $response->header( 'X-WP-TotalPages', $max_pages ); - $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); + $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); if ( $request['page'] > 1 ) { $prev_page = $request['page'] - 1; if ( $prev_page > $max_pages ) { @@ -378,7 +378,7 @@ public function create_item( $request ) { $response = $this->prepare_item_for_response( $comment, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); - $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $comment_id ) ) ); + $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment_id ) ) ); /** * Fires after a comment is created or updated via the REST API. @@ -605,16 +605,16 @@ public function prepare_item_for_response( $comment, $request ) { protected function prepare_links( $comment ) { $links = array( 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_ID ) ), + 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_ID ) ), ), 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), + 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), ), ); if ( 0 !== (int) $comment->user_id ) { $links['author'] = array( - 'href' => rest_url( '/wp/v2/users/' . $comment->user_id ), + 'href' => rest_url( 'wp/v2/users/' . $comment->user_id ), 'embeddable' => true, ); } @@ -626,7 +626,7 @@ protected function prepare_links( $comment ) { $base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name; $links['up'] = array( - 'href' => rest_url( '/wp/v2/' . $base . '/' . $comment->comment_post_ID ), + 'href' => rest_url( 'wp/v2/' . $base . '/' . $comment->comment_post_ID ), 'embeddable' => true, 'post_type' => $post->post_type, ); @@ -635,7 +635,7 @@ protected function prepare_links( $comment ) { if ( 0 !== (int) $comment->comment_parent ) { $links['in-reply-to'] = array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_parent ) ), + 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $comment->comment_parent ) ), 'embeddable' => true, ); } diff --git a/lib/endpoints/class-wp-rest-post-statuses-controller.php b/lib/endpoints/class-wp-rest-post-statuses-controller.php index d5c6e13605..faf73f0ea2 100755 --- a/lib/endpoints/class-wp-rest-post-statuses-controller.php +++ b/lib/endpoints/class-wp-rest-post-statuses-controller.php @@ -155,9 +155,9 @@ public function prepare_item_for_response( $status, $request ) { $response = rest_ensure_response( $data ); if ( 'publish' === $status->name ) { - $response->add_link( 'archives', rest_url( '/wp/v2/posts' ) ); + $response->add_link( 'archives', rest_url( 'wp/v2/posts' ) ); } else { - $response->add_link( 'archives', add_query_arg( 'status', $status->name, rest_url( '/wp/v2/posts' ) ) ); + $response->add_link( 'archives', add_query_arg( 'status', $status->name, rest_url( 'wp/v2/posts' ) ) ); } /** diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 469d763f0c..f700a86569 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -185,7 +185,7 @@ public function get_items( $request ) { unset( $request_params['filter']['posts_per_page'] ); unset( $request_params['filter']['paged'] ); } - $base = add_query_arg( $request_params, rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); + $base = add_query_arg( $request_params, rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); if ( $page > 1 ) { $prev_page = $page - 1; @@ -349,7 +349,7 @@ public function create_item( $request ) { $response = $this->prepare_item_for_response( $post, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); - $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) ); + $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $post_id ) ) ); return $response; } @@ -1204,7 +1204,7 @@ public function prepare_item_for_response( $post, $request ) { * @return array Links for the given post. */ protected function prepare_links( $post ) { - $base = sprintf( '/%s/%s', $this->namespace, $this->rest_base ); + $base = sprintf( '%s/%s', $this->namespace, $this->rest_base ); // Entity meta $links = array( @@ -1215,20 +1215,20 @@ protected function prepare_links( $post ) { 'href' => rest_url( $base ), ), 'about' => array( - 'href' => rest_url( '/wp/v2/types/' . $this->post_type ), + 'href' => rest_url( 'wp/v2/types/' . $this->post_type ), ), ); if ( ( in_array( $post->post_type, array( 'post', 'page' ) ) || post_type_supports( $post->post_type, 'author' ) ) && ! empty( $post->post_author ) ) { $links['author'] = array( - 'href' => rest_url( '/wp/v2/users/' . $post->post_author ), + 'href' => rest_url( 'wp/v2/users/' . $post->post_author ), 'embeddable' => true, ); }; if ( in_array( $post->post_type, array( 'post', 'page' ) ) || post_type_supports( $post->post_type, 'comments' ) ) { - $replies_url = rest_url( '/wp/v2/comments' ); + $replies_url = rest_url( 'wp/v2/comments' ); $replies_url = add_query_arg( 'post', $post->ID, $replies_url ); $links['replies'] = array( 'href' => $replies_url, diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index e4ada1c734..7e6cd3a2e0 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -213,7 +213,7 @@ public function prepare_item_for_response( $post, $request ) { $response = rest_ensure_response( $data ); if ( ! empty( $data['parent'] ) ) { - $response->add_link( 'parent', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->parent_base, $data['parent'] ) ) ); + $response->add_link( 'parent', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->parent_base, $data['parent'] ) ) ); } /** diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 5f525e59a2..a8509edb33 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -182,7 +182,7 @@ public function get_items( $request ) { $max_pages = ceil( $total_terms / $per_page ); $response->header( 'X-WP-TotalPages', (int) $max_pages ); - $base = add_query_arg( $request->get_query_params(), rest_url( '/' . $this->namespace . '/' . $this->rest_base ) ); + $base = add_query_arg( $request->get_query_params(), rest_url( $this->namespace . '/' . $this->rest_base ) ); if ( $page > 1 ) { $prev_page = $page - 1; if ( $prev_page > $max_pages ) { @@ -376,7 +376,7 @@ public function create_item( $request ) { $response = $this->prepare_item_for_response( $term, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); - $response->header( 'Location', rest_url( '/' . $this->namespace . '/' . $this->rest_base . '/' . $term->term_id ) ); + $response->header( 'Location', rest_url( $this->namespace . '/' . $this->rest_base . '/' . $term->term_id ) ); return $response; } @@ -599,7 +599,7 @@ public function prepare_item_for_response( $item, $request ) { * @return array Links for the given term. */ protected function prepare_links( $term ) { - $base = '/' . $this->namespace . '/' . $this->rest_base; + $base = $this->namespace . '/' . $this->rest_base; $links = array( 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $term->term_id ), diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 5b929d60f7..8cdc284b9a 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -164,7 +164,7 @@ public function get_items( $request ) { $max_pages = ceil( $total_users / $per_page ); $response->header( 'X-WP-TotalPages', (int) $max_pages ); - $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) ); + $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); if ( $page > 1 ) { $prev_page = $page - 1; if ( $prev_page > $max_pages ) { @@ -246,7 +246,7 @@ public function get_current_item( $request ) { $user = wp_get_current_user(); $response = $this->prepare_item_for_response( $user, $request ); $response = rest_ensure_response( $response ); - $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $current_user_id ) ) ); + $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $current_user_id ) ) ); $response->set_status( 302 ); return $response; @@ -331,7 +331,7 @@ public function create_item( $request ) { $response = $this->prepare_item_for_response( $user, $request ); $response = rest_ensure_response( $response ); $response->set_status( 201 ); - $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $user_id ) ) ); + $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $user_id ) ) ); return $response; } @@ -544,10 +544,10 @@ public function prepare_item_for_response( $user, $request ) { protected function prepare_links( $user ) { $links = array( 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $user->ID ) ), + 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $user->ID ) ), ), 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), + 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), ), ); diff --git a/tests/class-wp-test-rest-controller-testcase.php b/tests/class-wp-test-rest-controller-testcase.php index ce3de4cfdb..441908e0e4 100644 --- a/tests/class-wp-test-rest-controller-testcase.php +++ b/tests/class-wp-test-rest-controller-testcase.php @@ -6,7 +6,7 @@ abstract class WP_Test_REST_Controller_Testcase extends WP_Test_REST_TestCase { public function setUp() { parent::setUp(); - + add_filter( 'rest_url', array( $this, 'test_rest_url_for_leading_slash' ), 10, 2 ); /** @var WP_REST_Server $wp_rest_server */ global $wp_rest_server; $this->server = $wp_rest_server = new WP_Test_Spy_REST_Server; @@ -39,4 +39,8 @@ abstract public function test_prepare_item(); abstract public function test_get_item_schema(); + public function test_rest_url_for_leading_slash( $url, $path ) { + // Make sure path for rest_url has a leading slash for proper resolution. + $this->assertTrue( 0 === strpos( $path, '/' ) ); + } } diff --git a/tests/test-rest-categories-controller.php b/tests/test-rest-categories-controller.php index 77baecc8f5..8cc742a3d9 100644 --- a/tests/test-rest-categories-controller.php +++ b/tests/test-rest-categories-controller.php @@ -443,7 +443,7 @@ public function test_get_terms_pagination_headers() { $this->assertCount( 10, $response->get_data() ); $next_link = add_query_arg( array( 'page' => 2, - ), rest_url( '/wp/v2/categories' ) ); + ), rest_url( 'wp/v2/categories' ) ); $this->assertFalse( stripos( $headers['Link'], 'rel="prev"' ) ); $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); // 3rd page @@ -459,11 +459,11 @@ public function test_get_terms_pagination_headers() { $this->assertCount( 10, $response->get_data() ); $prev_link = add_query_arg( array( 'page' => 2, - ), rest_url( '/wp/v2/categories' ) ); + ), rest_url( 'wp/v2/categories' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $next_link = add_query_arg( array( 'page' => 4, - ), rest_url( '/wp/v2/categories' ) ); + ), rest_url( 'wp/v2/categories' ) ); $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); // Last page $request = new WP_REST_Request( 'GET', '/wp/v2/categories' ); @@ -475,7 +475,7 @@ public function test_get_terms_pagination_headers() { $this->assertCount( 1, $response->get_data() ); $prev_link = add_query_arg( array( 'page' => 5, - ), rest_url( '/wp/v2/categories' ) ); + ), rest_url( 'wp/v2/categories' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) ); // Out of bounds @@ -488,7 +488,7 @@ public function test_get_terms_pagination_headers() { $this->assertCount( 0, $response->get_data() ); $prev_link = add_query_arg( array( 'page' => 6, - ), rest_url( '/wp/v2/categories' ) ); + ), rest_url( 'wp/v2/categories' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) ); } @@ -759,7 +759,7 @@ public function test_prepare_taxonomy_term_child() { $this->assertEquals( 1, $data['parent'] ); $links = $response->get_links(); - $this->assertEquals( rest_url( '/wp/v2/categories/1' ), $links['up'][0]['href'] ); + $this->assertEquals( rest_url( 'wp/v2/categories/1' ), $links['up'][0]['href'] ); } public function test_get_item_schema() { diff --git a/tests/test-rest-tags-controller.php b/tests/test-rest-tags-controller.php index ad4c864259..91a69a6e4a 100644 --- a/tests/test-rest-tags-controller.php +++ b/tests/test-rest-tags-controller.php @@ -353,7 +353,7 @@ public function test_get_terms_pagination_headers() { $this->assertEquals( 5, $headers['X-WP-TotalPages'] ); $next_link = add_query_arg( array( 'page' => 2, - ), rest_url( '/wp/v2/tags' ) ); + ), rest_url( 'wp/v2/tags' ) ); $this->assertFalse( stripos( $headers['Link'], 'rel="prev"' ) ); $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); // 3rd page @@ -368,11 +368,11 @@ public function test_get_terms_pagination_headers() { $this->assertEquals( 6, $headers['X-WP-TotalPages'] ); $prev_link = add_query_arg( array( 'page' => 2, - ), rest_url( '/wp/v2/tags' ) ); + ), rest_url( 'wp/v2/tags' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $next_link = add_query_arg( array( 'page' => 4, - ), rest_url( '/wp/v2/tags' ) ); + ), rest_url( 'wp/v2/tags' ) ); $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); // Last page $request = new WP_REST_Request( 'GET', '/wp/v2/tags' ); @@ -383,7 +383,7 @@ public function test_get_terms_pagination_headers() { $this->assertEquals( 6, $headers['X-WP-TotalPages'] ); $prev_link = add_query_arg( array( 'page' => 5, - ), rest_url( '/wp/v2/tags' ) ); + ), rest_url( 'wp/v2/tags' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) ); // Out of bounds @@ -395,7 +395,7 @@ public function test_get_terms_pagination_headers() { $this->assertEquals( 6, $headers['X-WP-TotalPages'] ); $prev_link = add_query_arg( array( 'page' => 6, - ), rest_url( '/wp/v2/tags' ) ); + ), rest_url( 'wp/v2/tags' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) ); } diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 4327e937e4..9c16b84bfe 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -127,7 +127,7 @@ public function test_get_items_pagination_headers() { $this->assertEquals( 5, $headers['X-WP-TotalPages'] ); $next_link = add_query_arg( array( 'page' => 2, - ), rest_url( '/wp/v2/users' ) ); + ), rest_url( 'wp/v2/users' ) ); $this->assertFalse( stripos( $headers['Link'], 'rel="prev"' ) ); $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); // 3rd page @@ -142,11 +142,11 @@ public function test_get_items_pagination_headers() { $this->assertEquals( 6, $headers['X-WP-TotalPages'] ); $prev_link = add_query_arg( array( 'page' => 2, - ), rest_url( '/wp/v2/users' ) ); + ), rest_url( 'wp/v2/users' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $next_link = add_query_arg( array( 'page' => 4, - ), rest_url( '/wp/v2/users' ) ); + ), rest_url( 'wp/v2/users' ) ); $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); // Last page $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); @@ -157,7 +157,7 @@ public function test_get_items_pagination_headers() { $this->assertEquals( 6, $headers['X-WP-TotalPages'] ); $prev_link = add_query_arg( array( 'page' => 5, - ), rest_url( '/wp/v2/users' ) ); + ), rest_url( 'wp/v2/users' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) ); // Out of bounds @@ -169,7 +169,7 @@ public function test_get_items_pagination_headers() { $this->assertEquals( 6, $headers['X-WP-TotalPages'] ); $prev_link = add_query_arg( array( 'page' => 6, - ), rest_url( '/wp/v2/users' ) ); + ), rest_url( 'wp/v2/users' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) ); } @@ -201,7 +201,7 @@ public function test_get_items_page() { $prev_link = add_query_arg( array( 'per_page' => 5, 'page' => 1, - ), rest_url( '/wp/v2/users' ) ); + ), rest_url( 'wp/v2/users' ) ); $headers = $response->get_headers(); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); } @@ -487,7 +487,7 @@ public function test_get_current_user() { $headers = $response->get_headers(); $this->assertArrayHasKey( 'Location', $headers ); - $this->assertEquals( rest_url( '/wp/v2/users/' . $this->user ), $headers['Location'] ); + $this->assertEquals( rest_url( 'wp/v2/users/' . $this->user ), $headers['Location'] ); } public function test_get_current_user_without_permission() { From 7a36c337e6bcec244ca6edda3e1050e3597c89f2 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Thu, 7 Apr 2016 21:43:25 -0400 Subject: [PATCH 037/318] #2426-inconsistent-type-for-user-caps Fixes #2426, returns an empty object for users without capabilities. --- lib/endpoints/class-wp-rest-users-controller.php | 8 ++++++++ tests/test-rest-users-controller.php | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 5b929d60f7..49a2693952 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -516,6 +516,14 @@ public function prepare_item_for_response( $user, $request ) { $data['avatar_urls'] = rest_get_avatar_urls( $user->user_email ); } + if ( empty( $data['capabilities'] ) ) { + $data['capabilities'] = new stdClass(); + } + + if ( empty( $data['extra_capabilities'] ) ) { + $data['extra_capabilities'] = new stdClass(); + } + $context = ! empty( $request['context'] ) ? $request['context'] : 'embed'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 4327e937e4..9bd9227fc3 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -414,6 +414,22 @@ public function test_get_user_invalid_id() { $this->assertErrorResponse( 'rest_user_invalid_id', $response, 404 ); } + public function test_get_user_empty_capabilities() { + wp_set_current_user( $this->user ); + + $lolz = $this->factory->user->create( array( 'display_name' => 'lolz', 'roles' => '' ) ); + $lolz_user = get_user_by( 'id', $lolz ); + $lolz_user->remove_role( 'subscriber' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/users/' . $lolz ); + $request->set_param( 'context', 'edit' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $std_class = new stdClass(); + $this->assertEquals( $data['capabilities'], $std_class ); + $this->assertEquals( $data['extra_capabilities'], $std_class ); + } + public function test_get_item_without_permission() { wp_set_current_user( $this->editor ); From 1ede04b31f4447e560a31bd48bf29823fe8c3295 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Thu, 7 Apr 2016 22:01:32 -0400 Subject: [PATCH 038/318] Attempt to fix multisite tests. Attempting to fix multisite tests. --- tests/test-rest-users-controller.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 9bd9227fc3..70ede14a9f 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -416,10 +416,11 @@ public function test_get_user_invalid_id() { public function test_get_user_empty_capabilities() { wp_set_current_user( $this->user ); + $this->allow_user_to_manage_multisite(); $lolz = $this->factory->user->create( array( 'display_name' => 'lolz', 'roles' => '' ) ); - $lolz_user = get_user_by( 'id', $lolz ); - $lolz_user->remove_role( 'subscriber' ); + delete_user_option( $lolz, 'capabilities' ); + delete_user_option( $lolz, 'user_level' ); $request = new WP_REST_Request( 'GET', '/wp/v2/users/' . $lolz ); $request->set_param( 'context', 'edit' ); $response = $this->server->dispatch( $request ); From 779983e27966f1235161f015f660d25396da144b Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Thu, 7 Apr 2016 23:52:17 -0400 Subject: [PATCH 039/318] Fix tests for multisite. Not really sure why this only triggered on multisite. It seems odd. --- lib/endpoints/class-wp-rest-users-controller.php | 15 +++++++++------ tests/test-rest-users-controller.php | 12 ++++++++++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 49a2693952..76dc4886f0 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -516,15 +516,18 @@ public function prepare_item_for_response( $user, $request ) { $data['avatar_urls'] = rest_get_avatar_urls( $user->user_email ); } - if ( empty( $data['capabilities'] ) ) { - $data['capabilities'] = new stdClass(); - } + $context = ! empty( $request['context'] ) ? $request['context'] : 'embed'; + + if ( 'edit' === $context ) { + if ( empty( $data['capabilities'] ) ) { + $data['capabilities'] = new stdClass(); + } - if ( empty( $data['extra_capabilities'] ) ) { - $data['extra_capabilities'] = new stdClass(); + if ( empty( $data['extra_capabilities'] ) ) { + $data['extra_capabilities'] = new stdClass(); + } } - $context = ! empty( $request['context'] ) ? $request['context'] : 'embed'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 70ede14a9f..7f41632855 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -1096,8 +1096,16 @@ protected function check_user_data( $user, $data, $context, $links ) { $this->assertEquals( $user->last_name, $data['last_name'] ); $this->assertEquals( $user->nickname, $data['nickname'] ); $this->assertEquals( $user->user_email, $data['email'] ); - $this->assertEquals( $user->allcaps, $data['capabilities'] ); - $this->assertEquals( $user->caps, $data['extra_capabilities'] ); + if ( 'object' === gettype( $data['capabilities'] ) ) { + $this->assertEquals( new stdClass(), $data['capabilities'] ); + } else { + $this->assertEquals( $user->allcaps, $data['capabilities'] ); + } + if ( 'object' === gettype( $data['extra_capabilities'] ) ) { + $this->assertEquals( new stdClass(), $data['capabilities'] ); + } else { + $this->assertEquals( $user->caps, $data['extra_capabilities'] ); + } $this->assertEquals( date( 'c', strtotime( $user->user_registered ) ), $data['registered_date'] ); $this->assertEquals( $user->user_login, $data['username'] ); $this->assertEquals( $user->roles, $data['roles'] ); From d0c55b611e4b55c45c68bf213be42864005f4f32 Mon Sep 17 00:00:00 2001 From: Websupporter Date: Fri, 8 Apr 2016 09:59:38 +0300 Subject: [PATCH 040/318] Include Post data on the response object when declared #2416 the core fields shouldn't be included by default unless they're a part of the schema --- .../class-wp-rest-posts-controller.php | 73 +++++++++++++------ 1 file changed, 51 insertions(+), 22 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 0ad7e60ce9..28cd7b880a 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1071,26 +1071,58 @@ public function prepare_item_for_response( $post, $request ) { $GLOBALS['post'] = $post; setup_postdata( $post ); + $schema = $this->get_item_schema(); + // Base fields for every post. - $data = array( - 'id' => $post->ID, - 'date' => $this->prepare_date_response( $post->post_date_gmt, $post->post_date ), - 'date_gmt' => $this->prepare_date_response( $post->post_date_gmt ), - 'guid' => array( + $data = array(); + + if ( ! empty( $schema['properties']['id'] ) ) { + $data['id'] = $post->ID; + } + + if ( ! empty( $schema['properties']['date'] ) ) { + $data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date ); + } + + if ( ! empty( $schema['properties']['date_gmt'] ) ) { + $data['date_gmt'] = $this->prepare_date_response( $post->post_date_gmt ); + } + + if ( ! empty( $schema['properties']['guid'] ) ) { + $data['guid'] = array( /** This filter is documented in wp-includes/post-template.php */ 'rendered' => apply_filters( 'get_the_guid', $post->guid ), 'raw' => $post->guid, - ), - 'modified' => $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified ), - 'modified_gmt' => $this->prepare_date_response( $post->post_modified_gmt ), - 'password' => $post->post_password, - 'slug' => $post->post_name, - 'status' => $post->post_status, - 'type' => $post->post_type, - 'link' => get_permalink( $post->ID ), - ); + ); + } - $schema = $this->get_item_schema(); + if ( ! empty( $schema['properties']['modified'] ) ) { + $data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified ); + } + + if ( ! empty( $schema['properties']['modified_gmt'] ) ) { + $data['modified_gmt'] = $this->prepare_date_response( $post->post_modified_gmt ); + } + + if ( ! empty( $schema['properties']['password'] ) ) { + $data['password'] = $post->post_password; + } + + if ( ! empty( $schema['properties']['slug'] ) ) { + $data['slug'] = $post->post_name; + } + + if ( ! empty( $schema['properties']['status'] ) ) { + $data['status'] = $post->post_status; + } + + if ( ! empty( $schema['properties']['type'] ) ) { + $data['type'] = $post->post_type; + } + + if ( ! empty( $schema['properties']['link'] ) ) { + $data['link'] = get_permalink( $post->ID ); + } if ( ! empty( $schema['properties']['title'] ) ) { $data['title'] = array( @@ -1171,15 +1203,12 @@ public function prepare_item_for_response( $post, $request ) { $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); foreach ( $taxonomies as $taxonomy ) { $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; - $terms = get_the_terms( $post, $taxonomy->name ); - $data[ $base ] = $terms ? wp_list_pluck( $terms, 'term_id' ) : array(); + if ( ! empty( $schema['properties'][ $base ] ) ) { + $terms = get_the_terms( $post, $taxonomy->name ); + $data[ $base ] = $terms ? wp_list_pluck( $terms, 'term_id' ) : array(); + } } - //Remove all data, which is not part of the scheme - foreach( $data as $key => $property ) - if( ! isset( $schema['properties'][ $key ] ) || empty( $schema['properties'][ $key ] ) ) - unset( $data[ $key ] ); - $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); From cda055bdb29d25b448025e0b0ec9d61e9ac06eed Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Fri, 8 Apr 2016 10:54:13 -0400 Subject: [PATCH 041/318] Fixing tests. Thank you danielbachuber. Test now works, but I am not sure if the test should work differently. Currently it only checks if a leading slash is on the path which it should be. However, it is difficult to get the untouched data checked to see if it has a leading slash or not, which is the intention of the test. --- tests/class-wp-test-rest-controller-testcase.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/class-wp-test-rest-controller-testcase.php b/tests/class-wp-test-rest-controller-testcase.php index 441908e0e4..28092554ec 100644 --- a/tests/class-wp-test-rest-controller-testcase.php +++ b/tests/class-wp-test-rest-controller-testcase.php @@ -6,7 +6,7 @@ abstract class WP_Test_REST_Controller_Testcase extends WP_Test_REST_TestCase { public function setUp() { parent::setUp(); - add_filter( 'rest_url', array( $this, 'test_rest_url_for_leading_slash' ), 10, 2 ); + add_filter( 'rest_url', array( $this, 'filter_rest_url_for_leading_slash' ), 10, 2 ); /** @var WP_REST_Server $wp_rest_server */ global $wp_rest_server; $this->server = $wp_rest_server = new WP_Test_Spy_REST_Server; @@ -15,7 +15,7 @@ public function setUp() { public function tearDown() { parent::tearDown(); - + remove_filter( 'rest_url', array( $this, 'test_rest_url_for_leading_slash' ), 10, 2 ); /** @var WP_REST_Server $wp_rest_server */ global $wp_rest_server; $wp_rest_server = null; @@ -39,8 +39,10 @@ abstract public function test_prepare_item(); abstract public function test_get_item_schema(); - public function test_rest_url_for_leading_slash( $url, $path ) { + public function filter_rest_url_for_leading_slash( $url, $path ) { // Make sure path for rest_url has a leading slash for proper resolution. $this->assertTrue( 0 === strpos( $path, '/' ) ); + + return $url; } } From ee3c7ff18723ad0aa5a897dda8dc73478ec66fe4 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Sat, 9 Apr 2016 23:00:17 -0400 Subject: [PATCH 042/318] Set capabilities to null when empty Sets capabilities to null when empty. This matches with how other empty values are handled in the API. --- .../class-wp-rest-users-controller.php | 16 +++++++--------- tests/test-rest-users-controller.php | 17 ++++------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 76dc4886f0..3e4d929b64 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -518,14 +518,12 @@ public function prepare_item_for_response( $user, $request ) { $context = ! empty( $request['context'] ) ? $request['context'] : 'embed'; - if ( 'edit' === $context ) { - if ( empty( $data['capabilities'] ) ) { - $data['capabilities'] = new stdClass(); - } + if ( isset( $data['capabilities'] ) && empty( $data['capabilities'] ) ) { + $data['capabilities'] = null; + } - if ( empty( $data['extra_capabilities'] ) ) { - $data['extra_capabilities'] = new stdClass(); - } + if ( isset( $data['extra_capabilities'] ) && empty( $data['extra_capabilities'] ) ) { + $data['extra_capabilities'] = null; } $data = $this->add_additional_fields_to_object( $data, $request ); @@ -777,12 +775,12 @@ public function get_item_schema() { ), 'capabilities' => array( 'description' => __( 'All capabilities assigned to the resource.' ), - 'type' => 'object', + 'type' => array( 'object', 'null' ), 'context' => array( 'edit' ), ), 'extra_capabilities' => array( 'description' => __( 'Any extra capabilities assigned to the resource.' ), - 'type' => 'object', + 'type' => array( 'object', 'null' ), 'context' => array( 'edit' ), 'readonly' => true, ), diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 7f41632855..891dd0de17 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -426,9 +426,8 @@ public function test_get_user_empty_capabilities() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $std_class = new stdClass(); - $this->assertEquals( $data['capabilities'], $std_class ); - $this->assertEquals( $data['extra_capabilities'], $std_class ); + $this->assertEquals( $data['capabilities'], null ); + $this->assertEquals( $data['extra_capabilities'], null ); } public function test_get_item_without_permission() { @@ -1096,16 +1095,8 @@ protected function check_user_data( $user, $data, $context, $links ) { $this->assertEquals( $user->last_name, $data['last_name'] ); $this->assertEquals( $user->nickname, $data['nickname'] ); $this->assertEquals( $user->user_email, $data['email'] ); - if ( 'object' === gettype( $data['capabilities'] ) ) { - $this->assertEquals( new stdClass(), $data['capabilities'] ); - } else { - $this->assertEquals( $user->allcaps, $data['capabilities'] ); - } - if ( 'object' === gettype( $data['extra_capabilities'] ) ) { - $this->assertEquals( new stdClass(), $data['capabilities'] ); - } else { - $this->assertEquals( $user->caps, $data['extra_capabilities'] ); - } + $this->assertEquals( $user->allcaps, $data['capabilities'] ); + $this->assertEquals( $user->caps, $data['extra_capabilities'] ); $this->assertEquals( date( 'c', strtotime( $user->user_registered ) ), $data['registered_date'] ); $this->assertEquals( $user->user_login, $data['username'] ); $this->assertEquals( $user->roles, $data['roles'] ); From f6c59a52dffdf8f70ba3845f38da29af499209ea Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Sat, 9 Apr 2016 23:30:20 -0400 Subject: [PATCH 043/318] Fix test for multisite. Not sure why this is test only flags on multisite. Makes me somewhat concerned as I assume it should probably flag on both if it fails. --- tests/test-rest-users-controller.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 891dd0de17..35097e99f2 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -1095,8 +1095,16 @@ protected function check_user_data( $user, $data, $context, $links ) { $this->assertEquals( $user->last_name, $data['last_name'] ); $this->assertEquals( $user->nickname, $data['nickname'] ); $this->assertEquals( $user->user_email, $data['email'] ); - $this->assertEquals( $user->allcaps, $data['capabilities'] ); - $this->assertEquals( $user->caps, $data['extra_capabilities'] ); + if ( empty( $user->allcaps ) ) { + $this->assertEquals( null, $data['capabilities'] ); + } else { + $this->assertEquals( $user->allcaps, $data['capabilities'] ); + } + if ( empty( $user->caps ) ) { + $this->assertEquals( null, $data['extra_capabilities'] ); + } else { + $this->assertEquals( $user->caps, $data['extra_capabilities'] ); + } $this->assertEquals( date( 'c', strtotime( $user->user_registered ) ), $data['registered_date'] ); $this->assertEquals( $user->user_login, $data['username'] ); $this->assertEquals( $user->roles, $data['roles'] ); From 0ef3f2c60bb540dd5ec171d6b615eeeb78677dd5 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 13 Apr 2016 04:51:35 -0700 Subject: [PATCH 044/318] Avoid unnecessary SQL query by passing `$user_nicename` If no `$user_nicename` is supplied, `get_author_posts_url()` calls `get_userdata()` to get the `$user_nicename`. Because we already have it, we don't need to incur an unnecessary database hit. --- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 5b929d60f7..1c617f34c0 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -501,7 +501,7 @@ public function prepare_item_for_response( $user, $request ) { 'email' => $user->user_email, 'url' => $user->user_url, 'description' => $user->description, - 'link' => get_author_posts_url( $user->ID ), + 'link' => get_author_posts_url( $user->ID, $user->user_nicename ), 'nickname' => $user->nickname, 'slug' => $user->user_nicename, 'registered_date' => date( 'c', strtotime( $user->user_registered ) ), From 365628f08f762c0cfcf0751c4699a01e568bda21 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 13 Apr 2016 05:09:06 -0700 Subject: [PATCH 045/318] Avoid unnecessary SQL SELECT against user meta Because we're fetching all fields, `WP_User_Query` primes our user objects with their role / capabilities. This data is stored in user meta. But, core [isn't yet smart enough](https://core.trac.wordpress.org/ticket/36508) to precache user meta when `fields=>all`. We can be smarter than core by using `fields=>all_with_meta` --- lib/endpoints/class-wp-rest-users-controller.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 5b929d60f7..6881c25ec7 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -93,7 +93,9 @@ public function get_items_permissions_check( $request ) { */ public function get_items( $request ) { - $prepared_args = array(); + $prepared_args = array( + 'fields' => 'all_with_meta', + ); $prepared_args['exclude'] = $request['exclude']; $prepared_args['include'] = $request['include']; $prepared_args['order'] = $request['order']; From 1f6f578a26e734b45c040624471c90f80d690dfc Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 13 Apr 2016 06:42:17 -0700 Subject: [PATCH 046/318] Mark more post properties as readonly These properties are truly readonly; if you try to update them, nothing will update. --- lib/endpoints/class-wp-rest-posts-controller.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 469d763f0c..06f68992cb 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1331,11 +1331,13 @@ public function get_item_schema() { 'description' => __( 'GUID for the object, as it exists in the database.' ), 'type' => 'string', 'context' => array( 'edit' ), + 'readonly' => true, ), 'rendered' => array( 'description' => __( 'GUID for the object, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), + 'readonly' => true, ), ), ), @@ -1466,6 +1468,7 @@ public function get_item_schema() { 'description' => __( 'HTML title for the object, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, ), ), ); @@ -1486,6 +1489,7 @@ public function get_item_schema() { 'description' => __( 'HTML content for the object, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), + 'readonly' => true, ), ), ); @@ -1514,6 +1518,7 @@ public function get_item_schema() { 'description' => __( 'HTML excerpt for the object, transformed for display.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, ), ), ); From 485a1b2776c1ac463ce107ce8335b41c3874dcda Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 13 Apr 2016 08:10:01 -0700 Subject: [PATCH 047/318] Only return or update user properties when defined in schema If a WordPress has removed the field from the schema, it shouldn't be included in the response, or be unexpectedly updated in the database. --- .../class-wp-rest-users-controller.php | 105 +++++++++++++----- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 9d3842f8cf..db4eb1d702 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -280,7 +280,9 @@ public function create_item( $request ) { return new WP_Error( 'rest_user_exists', __( 'Cannot create existing resource.' ), array( 'status' => 400 ) ); } - if ( ! empty( $request['roles'] ) ) { + $schema = $this->get_item_schema(); + + if ( ! empty( $request['roles'] ) && ! empty( $schema['properties']['roles'] ) ) { $check_permission = $this->check_role_update( $request['id'], $request['roles'] ); if ( is_wp_error( $check_permission ) ) { return $check_permission; @@ -314,7 +316,7 @@ public function create_item( $request ) { } $user = get_user_by( 'id', $user_id ); - if ( ! empty( $request['roles'] ) ) { + if ( ! empty( $request['roles'] ) && ! empty( $schema['properties']['roles'] ) ) { array_map( array( $user, 'add_role' ), $request['roles'] ); } @@ -494,25 +496,68 @@ public function delete_item( $request ) { * @return WP_REST_Response $response Response data. */ public function prepare_item_for_response( $user, $request ) { - $data = array( - 'id' => $user->ID, - 'username' => $user->user_login, - 'name' => $user->display_name, - 'first_name' => $user->first_name, - 'last_name' => $user->last_name, - 'email' => $user->user_email, - 'url' => $user->user_url, - 'description' => $user->description, - 'link' => get_author_posts_url( $user->ID, $user->user_nicename ), - 'nickname' => $user->nickname, - 'slug' => $user->user_nicename, - 'registered_date' => date( 'c', strtotime( $user->user_registered ) ), - 'roles' => $user->roles, - 'capabilities' => $user->allcaps, - 'extra_capabilities' => $user->caps, - ); + $data = array(); $schema = $this->get_item_schema(); + if ( ! empty( $schema['properties']['id'] ) ) { + $data['id'] = $user->ID; + } + + if ( ! empty( $schema['properties']['username'] ) ) { + $data['username'] = $user->user_login; + } + + if ( ! empty( $schema['properties']['name'] ) ) { + $data['name'] = $user->display_name; + } + + if ( ! empty( $schema['properties']['first_name'] ) ) { + $data['first_name'] = $user->first_name; + } + + if ( ! empty( $schema['properties']['last_name'] ) ) { + $data['last_name'] = $user->last_name; + } + + if ( ! empty( $schema['properties']['email'] ) ) { + $data['email'] = $user->user_email; + } + + if ( ! empty( $schema['properties']['url'] ) ) { + $data['url'] = $user->user_url; + } + + if ( ! empty( $schema['properties']['description'] ) ) { + $data['description'] = $user->description; + } + + if ( ! empty( $schema['properties']['link'] ) ) { + $data['link'] = get_author_posts_url( $user->ID, $user->user_nicename ); + } + + if ( ! empty( $schema['properties']['nickname'] ) ) { + $data['nickname'] = $user->nickname; + } + + if ( ! empty( $schema['properties']['slug'] ) ) { + $data['slug'] = $user->user_nicename; + } + + if ( ! empty( $schema['properties']['roles'] ) ) { + $data['roles'] = $user->roles; + } + + if ( ! empty( $schema['properties']['registered_date'] ) ) { + $data['registered_date'] = date( 'c', strtotime( $user->user_registered ) ); + } + + if ( ! empty( $schema['properties']['capabilities'] ) ) { + $data['capabilities'] = $user->allcaps; + } + + if ( ! empty( $schema['properties']['extra_capabilities'] ) ) { + $data['extra_capabilities'] = $user->caps; + } if ( ! empty( $schema['properties']['avatar_urls'] ) ) { $data['avatar_urls'] = rest_get_avatar_urls( $user->user_email ); @@ -565,14 +610,16 @@ protected function prepare_links( $user ) { protected function prepare_item_for_database( $request ) { $prepared_user = new stdClass; + $schema = $this->get_item_schema(); + // required arguments. - if ( isset( $request['email'] ) ) { + if ( isset( $request['email'] ) && ! empty( $schema['properties']['email'] ) ) { $prepared_user->user_email = $request['email']; } - if ( isset( $request['username'] ) ) { + if ( isset( $request['username'] ) && ! empty( $schema['properties']['username'] ) ) { $prepared_user->user_login = $request['username']; } - if ( isset( $request['password'] ) ) { + if ( isset( $request['password'] ) && ! empty( $schema['properties']['password'] ) ) { $prepared_user->user_pass = $request['password']; } @@ -580,26 +627,26 @@ protected function prepare_item_for_database( $request ) { if ( isset( $request['id'] ) ) { $prepared_user->ID = absint( $request['id'] ); } - if ( isset( $request['name'] ) ) { + if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) { $prepared_user->display_name = $request['name']; } - if ( isset( $request['first_name'] ) ) { + if ( isset( $request['first_name'] ) && ! empty( $schema['properties']['first_name'] ) ) { $prepared_user->first_name = $request['first_name']; } - if ( isset( $request['last_name'] ) ) { + if ( isset( $request['last_name'] ) && ! empty( $schema['properties']['last_name'] ) ) { $prepared_user->last_name = $request['last_name']; } - if ( isset( $request['nickname'] ) ) { + if ( isset( $request['nickname'] ) && ! empty( $schema['properties']['nickname'] ) ) { $prepared_user->nickname = $request['nickname']; } - if ( isset( $request['slug'] ) ) { + if ( isset( $request['slug'] ) && ! empty( $schema['properties']['slug'] ) ) { $prepared_user->user_nicename = $request['slug']; } - if ( isset( $request['description'] ) ) { + if ( isset( $request['description'] ) && ! empty( $schema['properties']['description'] ) ) { $prepared_user->description = $request['description']; } - if ( isset( $request['url'] ) ) { + if ( isset( $request['url'] ) && ! empty( $schema['properties']['url'] ) ) { $prepared_user->user_url = $request['url']; } From eab414485f5c2d47cd8b47cb4f55d513b7a53b95 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 13 Apr 2016 08:19:34 -0700 Subject: [PATCH 048/318] Mark Users' `capabilities` property as readonly It's not actually editable through the API --- lib/endpoints/class-wp-rest-users-controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 9d3842f8cf..3579997ff9 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -770,6 +770,7 @@ public function get_item_schema() { 'description' => __( 'All capabilities assigned to the resource.' ), 'type' => 'object', 'context' => array( 'edit' ), + 'readonly' => true, ), 'extra_capabilities' => array( 'description' => __( 'Any extra capabilities assigned to the resource.' ), From 418180940bd66e3385ffd3b82fdf4a68a5c9feba Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 13 Apr 2016 08:59:55 -0700 Subject: [PATCH 049/318] Revert "Avoid unnecessary SQL SELECT against user meta" --- lib/endpoints/class-wp-rest-users-controller.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 9d3842f8cf..1c617f34c0 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -93,9 +93,7 @@ public function get_items_permissions_check( $request ) { */ public function get_items( $request ) { - $prepared_args = array( - 'fields' => 'all_with_meta', - ); + $prepared_args = array(); $prepared_args['exclude'] = $request['exclude']; $prepared_args['include'] = $request['include']; $prepared_args['order'] = $request['order']; From 8d0e51a65b3e222f52a6b35c221204481f8774ea Mon Sep 17 00:00:00 2001 From: Websupporter Date: Fri, 22 Apr 2016 13:22:31 +0300 Subject: [PATCH 050/318] Use WPINC instead of wp-includes/ `class-phpass.php` was required with an hardcoded `wp-includes`. Switched it to WPINC constant --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 06f68992cb..7eeb79ea8d 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -698,7 +698,7 @@ protected function prepare_password_response( $password ) { * Fake the correct cookie to fool post_password_required(). * Without this, get_the_content() will give a password form. */ - require_once ABSPATH . 'wp-includes/class-phpass.php'; + require_once ABSPATH . WPINC .'/class-phpass.php'; $hasher = new PasswordHash( 8, true ); $value = $hasher->HashPassword( $password ); $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = wp_slash( $value ); From a022a75c439ec39e6f136e9c8c79046fe087b5e6 Mon Sep 17 00:00:00 2001 From: Websupporter Date: Sun, 24 Apr 2016 21:32:04 +0300 Subject: [PATCH 051/318] return error, if /user?context=edit and !current_user_can( 'list_users' ) Solves https://github.com/WP-API/WP-API/issues/2382 Instead of the capability 'edit_user' I took 'list_users' like it is also done in the `get_item_permissions_check()`. Did make sense to me. --- lib/endpoints/class-wp-rest-users-controller.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 725ad51a0c..57125f09bf 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -82,6 +82,10 @@ public function get_items_permissions_check( $request ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot filter by role.' ), array( 'status' => rest_authorization_required_code() ) ); } + if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) { + return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view this resource with edit context.' ), array( 'status' => rest_authorization_required_code() ) ); + } + return true; } From 84c43b489c8715fb2474338c5bfa7b9c1124d2f8 Mon Sep 17 00:00:00 2001 From: Websupporter Date: Mon, 25 Apr 2016 09:58:09 +0300 Subject: [PATCH 052/318] test get items with context edit check checks, if we get the right responses on users get_items() with `context` `edit` --- tests/test-rest-users-controller.php | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 4327e937e4..886e945b44 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -86,6 +86,38 @@ public function test_get_items() { $this->check_user_data( $userdata, $data, 'view', $data['_links'] ); } + public function test_get_items_with_edit_context() { + wp_set_current_user( $this->user ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'context', 'edit' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $all_data = $response->get_data(); + $data = $all_data[0]; + $userdata = get_userdata( $data['id'] ); + $this->check_user_data( $userdata, $data, 'edit', $data['_links'] ); + } + + public function test_get_items_with_edit_context_without_permission() { + //test with a user not logged in + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'context', 'edit' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 401, $response->get_status() ); + + //test with a user logged in but without sufficient capabilities; capability in question: 'list_users' + wp_set_current_user( $this->editor ); + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'context', 'edit' ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 403, $response->get_status() ); + } + public function test_get_items_unauthenticated_only_shows_public_users() { $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); $response = $this->server->dispatch( $request ); From db5b6e5f64758aa141b4fdb88db5f3d9045039d1 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Wed, 27 Apr 2016 08:23:17 -0700 Subject: [PATCH 053/318] Conditionally model the term response based on its schema Similarly, only permit editing fields present on the schema --- .../class-wp-rest-terms-controller.php | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 5f525e59a2..2e905b2e90 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -513,23 +513,24 @@ public function delete_item( $request ) { public function prepare_item_for_database( $request ) { $prepared_term = new stdClass; - if ( isset( $request['name'] ) ) { + $schema = $this->get_item_schema(); + if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) { $prepared_term->name = $request['name']; } - if ( isset( $request['slug'] ) ) { + if ( isset( $request['slug'] ) && ! empty( $schema['properties']['slug'] ) ) { $prepared_term->slug = $request['slug']; } - if ( isset( $request['taxonomy'] ) ) { + if ( isset( $request['taxonomy'] ) && ! empty( $schema['properties']['taxonomy'] ) ) { $prepared_term->taxonomy = $request['taxonomy']; } - if ( isset( $request['description'] ) ) { + if ( isset( $request['description'] ) && ! empty( $schema['properties']['description'] ) ) { $prepared_term->description = $request['description']; } - if ( isset( $request['parent'] ) ) { + if ( isset( $request['parent'] ) && ! empty( $schema['properties']['parent'] ) ) { $parent_term_id = 0; $parent_term = get_term( (int) $request['parent'], $this->taxonomy ); @@ -558,16 +559,29 @@ public function prepare_item_for_database( $request ) { */ public function prepare_item_for_response( $item, $request ) { - $data = array( - 'id' => (int) $item->term_id, - 'count' => (int) $item->count, - 'description' => $item->description, - 'link' => get_term_link( $item ), - 'name' => $item->name, - 'slug' => $item->slug, - 'taxonomy' => $item->taxonomy, - ); $schema = $this->get_item_schema(); + $data = array(); + if ( ! empty( $schema['properties']['id'] ) ) { + $data['id'] = (int) $item->term_id; + } + if ( ! empty( $schema['properties']['count'] ) ) { + $data['count'] = (int) $item->count; + } + if ( ! empty( $schema['properties']['description'] ) ) { + $data['description'] = $item->description; + } + if ( ! empty( $schema['properties']['link'] ) ) { + $data['link'] = get_term_link( $item ); + } + if ( ! empty( $schema['properties']['name'] ) ) { + $data['name'] = $item->name; + } + if ( ! empty( $schema['properties']['slug'] ) ) { + $data['slug'] = $item->slug; + } + if ( ! empty( $schema['properties']['taxonomy'] ) ) { + $data['taxonomy'] = $item->taxonomy; + } if ( ! empty( $schema['properties']['parent'] ) ) { $data['parent'] = (int) $item->parent; } From 2ab7ef138c6cbb40d871020bad4b23c38b7b6822 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 3 May 2016 23:25:07 -0700 Subject: [PATCH 054/318] Add boolean type support for rest_validate_request_arg --- plugin.php | 4 ++++ tests/test-rest-controller.php | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/plugin.php b/plugin.php index 830d972447..965e07ee48 100755 --- a/plugin.php +++ b/plugin.php @@ -299,6 +299,10 @@ function rest_validate_request_arg( $value, $request, $param ) { return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $param, 'integer' ) ); } + if ( 'boolean' === $args['type'] && ! is_bool( $value ) ) { + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $param, 'boolean' ) ); + } + if ( 'string' === $args['type'] && ! is_string( $value ) ) { return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $param, 'string' ) ); } diff --git a/tests/test-rest-controller.php b/tests/test-rest-controller.php index 75910b576c..6131f6a3b4 100644 --- a/tests/test-rest-controller.php +++ b/tests/test-rest-controller.php @@ -9,6 +9,9 @@ public function setUp() { 'someinteger' => array( 'type' => 'integer', ), + 'someboolean' => array( + 'type' => 'boolean', + ), 'somestring' => array( 'type' => 'string', ), @@ -40,6 +43,25 @@ public function test_validate_schema_type_integer() { ); } + public function test_validate_schema_type_boolean() { + + $this->assertTrue( + rest_validate_request_arg( true, $this->request, 'someboolean' ) + ); + $this->assertTrue( + rest_validate_request_arg( false, $this->request, 'someboolean' ) + ); + + $this->assertErrorResponse( + 'rest_invalid_param', + rest_validate_request_arg( 'true', $this->request, 'someboolean' ) + ); + $this->assertErrorResponse( + 'rest_invalid_param', + rest_validate_request_arg( '123', $this->request, 'someboolean' ) + ); + } + public function test_validate_schema_type_string() { $this->assertTrue( From 9dc1c586a18244c6704f3512dfa670a8f3d8c3cb Mon Sep 17 00:00:00 2001 From: Websupporter Date: Wed, 4 May 2016 11:46:53 +0300 Subject: [PATCH 055/318] create/update requests don't process data not included in the schema >We also need to make sure create / update requests don't process data that's not included in the schema. https://github.com/WP-API/WP-API/issues/2416 This commit updates `prepare_item_for_database()` which is used by `update_item()` and `create_item()` to prepare the post. Here it also checks for the schema. --- lib/endpoints/class-wp-rest-posts-controller.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 6259ccfc4d..cafb7abaf0 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -722,6 +722,7 @@ protected function prepare_item_for_database( $request ) { } $schema = $this->get_item_schema(); + #echo json_encode( $schema );die(); // Post title. if ( ! empty( $schema['properties']['title'] ) && isset( $request['title'] ) ) { @@ -761,7 +762,7 @@ protected function prepare_item_for_database( $request ) { $post_type = get_post_type_object( $prepared_post->post_type ); // Post status. - if ( isset( $request['status'] ) ) { + if ( ! empty( $schema['properties']['status'] ) && isset( $request['status'] ) ) { $status = $this->handle_status_param( $request['status'], $post_type ); if ( is_wp_error( $status ) ) { return $status; @@ -771,13 +772,13 @@ protected function prepare_item_for_database( $request ) { } // Post date. - if ( ! empty( $request['date'] ) ) { + if ( ! empty( $schema['properties']['date'] ) && ! empty( $request['date'] ) ) { $date_data = rest_get_date_with_gmt( $request['date'] ); if ( ! empty( $date_data ) ) { list( $prepared_post->post_date, $prepared_post->post_date_gmt ) = $date_data; } - } elseif ( ! empty( $request['date_gmt'] ) ) { + } elseif ( ! empty( $schema['properties']['date_gmt'] ) && ! empty( $request['date_gmt'] ) ) { $date_data = rest_get_date_with_gmt( $request['date_gmt'], true ); if ( ! empty( $date_data ) ) { @@ -785,7 +786,7 @@ protected function prepare_item_for_database( $request ) { } } // Post slug. - if ( isset( $request['slug'] ) ) { + if ( ! empty( $schema['properties']['slug'] ) && isset( $request['slug'] ) ) { $prepared_post->post_name = $request['slug']; } @@ -802,7 +803,7 @@ protected function prepare_item_for_database( $request ) { } // Post password. - if ( isset( $request['password'] ) && '' !== $request['password'] ) { + if ( ! empty( $schema['properties']['password'] ) && isset( $request['password'] ) && '' !== $request['password'] ) { $prepared_post->post_password = $request['password']; if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) { @@ -814,7 +815,7 @@ protected function prepare_item_for_database( $request ) { } } - if ( ! empty( $request['sticky'] ) ) { + if ( ! empty( $schema['properties']['sticky'] ) && ! empty( $request['sticky'] ) ) { if ( ! empty( $prepared_post->ID ) && post_password_required( $prepared_post->ID ) ) { return new WP_Error( 'rest_invalid_field', __( 'A password protected post can not be set to sticky.' ), array( 'status' => 400 ) ); } From 0b55e3e328024e9b44bc4132786c2d3982480815 Mon Sep 17 00:00:00 2001 From: Websupporter Date: Wed, 4 May 2016 11:48:06 +0300 Subject: [PATCH 056/318] cleaning test-info --- lib/endpoints/class-wp-rest-posts-controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index cafb7abaf0..3776c2376b 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -722,7 +722,6 @@ protected function prepare_item_for_database( $request ) { } $schema = $this->get_item_schema(); - #echo json_encode( $schema );die(); // Post title. if ( ! empty( $schema['properties']['title'] ) && isset( $request['title'] ) ) { From 377c48d915a1c42654394f1c99449ad2a9bac8e3 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sat, 7 May 2016 13:24:34 -0500 Subject: [PATCH 057/318] Remove read-only attribute from author_ip --- lib/endpoints/class-wp-rest-comments-controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 697a8d4bac..88be5f8fe7 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -812,7 +812,6 @@ public function get_item_schema() { 'description' => __( 'IP address for the object author.' ), 'type' => 'string', 'context' => array( 'edit' ), - 'readonly' => true, ), 'author_name' => array( 'description' => __( 'Display name for the object author.' ), From 52542d316a8ae1bfd75e223135915d8a6978c176 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 8 May 2016 10:38:50 -0500 Subject: [PATCH 058/318] Use string format type of 'ipv4' to validate comment author IP address New function `rest_is_ip_address()` checks if a value is a valid v4 IP address. It uses regex because core cannot guarantee support for `filter_var`. --- .../class-wp-rest-comments-controller.php | 7 ++--- plugin.php | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 88be5f8fe7..002f983fe9 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -748,11 +748,7 @@ protected function prepare_item_for_database( $request ) { } if ( isset( $request['author_ip'] ) ) { - if ( filter_var( $request['author_ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) { - $prepared_comment['comment_author_IP'] = $request['author_ip']; - } else { - return new WP_Error( 'rest_comment_invalid_author_ip', __( 'The IP address you provided is invalid.' ), array( 'status' => 400 ) ); - } + $prepared_comment['comment_author_IP'] = $request['author_ip']; } if ( isset( $request['type'] ) ) { @@ -811,6 +807,7 @@ public function get_item_schema() { 'author_ip' => array( 'description' => __( 'IP address for the object author.' ), 'type' => 'string', + 'format' => 'ipv4', 'context' => array( 'edit' ), ), 'author_name' => array( diff --git a/plugin.php b/plugin.php index 965e07ee48..b3d733ddc0 100755 --- a/plugin.php +++ b/plugin.php @@ -320,6 +320,11 @@ function rest_validate_request_arg( $value, $request, $param ) { return new WP_Error( 'rest_invalid_email', __( 'The email address you provided is invalid.' ) ); } break; + case 'ipv4' : + if ( ! rest_is_ip_address( $value ) ) { + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.'), $value ) ); + } + break; } } @@ -395,6 +400,9 @@ function rest_sanitize_request_arg( $value, $request, $param ) { case 'uri' : return esc_url_raw( $value ); + + case 'ipv4' : + return sanitize_text_field( $value ); } } @@ -402,3 +410,23 @@ function rest_sanitize_request_arg( $value, $request, $param ) { } } + +if ( ! function_exists( 'rest_is_ip_address' ) ) { + /** + * Determines if a IPv4 address is valid. + * + * Does not handle IPv6 addresses. + * + * @param string $ipv4 IP 32-bit address. + * @return string|false The valid IPv4 address, otherwise false. + */ + function rest_is_ip_address( $ipv4 ) { + $pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/'; + + if ( ! preg_match( $pattern, $ipv4 ) ) { + return false; + } + + return $ipv4; + } +} From ebdcd2510485c6e5542320c7f91c073eb7c9b602 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 8 May 2016 10:39:50 -0500 Subject: [PATCH 059/318] Use the schema to set `127.0.0.1` as the default comment author IP --- lib/endpoints/class-wp-rest-comments-controller.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 002f983fe9..a97c248844 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -349,9 +349,7 @@ public function create_item( $request ) { if ( ! isset( $prepared_comment['comment_author_url'] ) ) { $prepared_comment['comment_author_url'] = ''; } - if ( ! isset( $prepared_comment['comment_author_IP'] ) ) { - $prepared_comment['comment_author_IP'] = '127.0.0.1'; - } + $prepared_comment['comment_agent'] = ''; $prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment ); @@ -809,6 +807,9 @@ public function get_item_schema() { 'type' => 'string', 'format' => 'ipv4', 'context' => array( 'edit' ), + 'arg_options' => array( + 'default' => '127.0.0.1', + ), ), 'author_name' => array( 'description' => __( 'Display name for the object author.' ), From 7f5c77ee492617b4b0c1fc4a3ac9dc400668e209 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 8 May 2016 10:40:39 -0500 Subject: [PATCH 060/318] Update tests and improve coverage --- tests/test-rest-comments-controller.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index fc2897812d..583da7e91c 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -643,6 +643,7 @@ public function test_create_item_assign_different_user() { $data = $response->get_data(); $this->assertEquals( $subscriber_id, $data['author'] ); + $this->assertEquals( '127.0.0.1', $data['author_ip'] ); } public function test_create_comment_without_type() { @@ -839,7 +840,8 @@ public function test_create_comment_invalid_author_IP() { $request->set_body( wp_json_encode( $params ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_comment_invalid_author_ip', $response, 400 ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } public function test_create_comment_no_post_id() { @@ -964,6 +966,7 @@ public function test_update_item() { 'author_name' => 'Disco Stu', 'author_url' => 'http://stusdisco.com', 'author_email' => 'stu@stusdisco.com', + 'author_ip' => '4.4.4.4', 'date' => '2014-11-07T10:14:25', 'karma' => 100, 'post' => $post_id, @@ -982,6 +985,7 @@ public function test_update_item() { $this->assertEquals( $params['author_name'], $comment['author_name'] ); $this->assertEquals( $params['author_url'], $comment['author_url'] ); $this->assertEquals( $params['author_email'], $comment['author_email'] ); + $this->assertEquals( $params['author_ip'], $comment['author_ip'] ); $this->assertEquals( $params['post'], $comment['post'] ); $this->assertEquals( $params['karma'], $comment['karma'] ); From a7b033f80dc9a95149059b5a9731a36745e90a59 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 8 May 2016 10:48:24 -0500 Subject: [PATCH 061/318] Add space to fix PHPCS error --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index b3d733ddc0..4ae237458c 100755 --- a/plugin.php +++ b/plugin.php @@ -322,7 +322,7 @@ function rest_validate_request_arg( $value, $request, $param ) { break; case 'ipv4' : if ( ! rest_is_ip_address( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.'), $value ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.' ), $value ) ); } break; } From d051e65d98df3eff31ccd28c8481141a5590159d Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 8 May 2016 11:35:21 -0500 Subject: [PATCH 062/318] Remove unneeded check on prepare_item_for_database returning a WP_Error on update --- lib/endpoints/class-wp-rest-comments-controller.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index a97c248844..7a58d9a495 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -433,9 +433,6 @@ public function update_item( $request ) { } $prepared_args = $this->prepare_item_for_database( $request ); - if ( is_wp_error( $prepared_args ) ) { - return $prepared_args; - } if ( empty( $prepared_args ) && isset( $request['status'] ) ) { // Only the comment status is being changed. From 6dde6ce0a5d5efda25374b002c148921083db2bc Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 8 May 2016 11:41:27 -0500 Subject: [PATCH 063/318] Similar to d051e65 this removes the unneeded check on comment create --- lib/endpoints/class-wp-rest-comments-controller.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 7a58d9a495..ea9825d82f 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -319,9 +319,6 @@ public function create_item( $request ) { } $prepared_comment = $this->prepare_item_for_database( $request ); - if ( is_wp_error( $prepared_comment ) ) { - return $prepared_comment; - } // Setting remaining values before wp_insert_comment so we can // use wp_allow_comment(). From 8baa63e915b11c09eff52c09e1708f92c84fc8af Mon Sep 17 00:00:00 2001 From: hideokamoto Date: Sun, 15 May 2016 14:48:01 +0900 Subject: [PATCH 064/318] Remove Unused Parameter in lib/endpoints/class-wp-rest-controller.php I run phpmd, and find following notification. /var/www/wordpress/wp-content/plugins/rest-api/lib/endpoints/class-wp-rest-controller.php:292Avoid unused local variables such as ''. So, I removed parameter --- lib/endpoints/class-wp-rest-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 4d9fd7a527..150f77d84c 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -289,7 +289,7 @@ public function get_context_param( $args = array() ) { return array_merge( $param_details, $args ); } $contexts = array(); - foreach ( $schema['properties'] as $key => $attributes ) { + foreach ( $schema['properties'] as $attributes ) { if ( ! empty( $attributes['context'] ) ) { $contexts = array_merge( $contexts, $attributes['context'] ); } From debb5bca37b1d918aa01f755a5cd39dee6fd754f Mon Sep 17 00:00:00 2001 From: Eddie Hurtig Date: Mon, 16 May 2016 11:18:09 -0700 Subject: [PATCH 065/318] Update Installation Docs where should I drop "this directory". --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d84a76971f..1162e73fd8 100755 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ There's no fixed timeline for integration into core at this time, but getting cl ## Installation -Drop this directory in and activate it. You need to be using pretty permalinks +Drop this directory into your plugins folder and activate it. You need to be using pretty permalinks to use the plugin, as it uses custom rewrite rules to power the API. Also, be sure to use the Subversion `trunk` branch of WordPress Core as there are potentially recent commits to Core that the REST API relies on. See the [WordPress.org website](https://wordpress.org/download/svn/) for simple instructions. From 4535d64a291bb1bfec89cec298bbcec4a6cf9d99 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Mon, 30 May 2016 11:52:27 -0400 Subject: [PATCH 066/318] Fixed redundant conditionals, went back to object Fixed redundant conditionals, went back to returning empty object instead of null. This better represents that the user currently has 0 capabilities as opposed to null capabilities. --- lib/endpoints/class-wp-rest-users-controller.php | 8 ++++---- tests/test-rest-users-controller.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 3e4d929b64..d8b2b3a4ab 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -518,12 +518,12 @@ public function prepare_item_for_response( $user, $request ) { $context = ! empty( $request['context'] ) ? $request['context'] : 'embed'; - if ( isset( $data['capabilities'] ) && empty( $data['capabilities'] ) ) { - $data['capabilities'] = null; + if ( empty( $data['capabilities'] ) ) { + $data['capabilities'] = new stdClass(); } - if ( isset( $data['extra_capabilities'] ) && empty( $data['extra_capabilities'] ) ) { - $data['extra_capabilities'] = null; + if ( empty( $data['extra_capabilities'] ) ) { + $data['extra_capabilities'] = new stdClass(); } $data = $this->add_additional_fields_to_object( $data, $request ); diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 35097e99f2..d8489154eb 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -1096,12 +1096,12 @@ protected function check_user_data( $user, $data, $context, $links ) { $this->assertEquals( $user->nickname, $data['nickname'] ); $this->assertEquals( $user->user_email, $data['email'] ); if ( empty( $user->allcaps ) ) { - $this->assertEquals( null, $data['capabilities'] ); + $this->assertEquals( new stdClass(), $data['capabilities'] ); } else { $this->assertEquals( $user->allcaps, $data['capabilities'] ); } if ( empty( $user->caps ) ) { - $this->assertEquals( null, $data['extra_capabilities'] ); + $this->assertEquals( new stdClass(), $data['extra_capabilities'] ); } else { $this->assertEquals( $user->caps, $data['extra_capabilities'] ); } From be939537854bc10add6bc33b8f9f64aece603169 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Mon, 30 May 2016 11:56:15 -0400 Subject: [PATCH 067/318] Fix Tests Testing fixes. --- tests/test-rest-users-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index c89234b7d6..6030031748 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -458,8 +458,8 @@ public function test_get_user_empty_capabilities() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertEquals( $data['capabilities'], null ); - $this->assertEquals( $data['extra_capabilities'], null ); + $this->assertEquals( $data['capabilities'], new stdClass() ); + $this->assertEquals( $data['extra_capabilities'], new stdClass() ); } public function test_get_item_without_permission() { From a0e0040b72f71fafdd8624893b7b8c31392a4ddb Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Tue, 31 May 2016 16:36:02 -0500 Subject: [PATCH 068/318] Wrap lines at 80 characters --- README.md | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1162e73fd8..85e8a3d5f8 100755 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ Access your WordPress site's data through an easy-to-use HTTP REST API. ## WARNING -The **"develop"** branch is undergoing substantial changes and is **NOT COMPLETE OR STABLE**. [Read the in-progress documentation](http://v2.wp-api.org/) to introduce yourself to endpoints, internal patterns, and implementation details. +The **"develop"** branch is undergoing substantial changes and is **NOT +COMPLETE OR STABLE**. [Read the in-progress documentation](http://v2.wp-api.org/) +to introduce yourself to endpoints, internal patterns, and implementation details. The **"master"** branch represents a **BETA** of our next version release. @@ -30,9 +32,9 @@ Update user with ID 4? Send a `POST` request to `/wp-json/wp/v2/users/4`. Get al posts with the search term "awesome"? `GET /wp-json/wp/v2/posts?filter[s]=awesome`. It's that easy. -WP API exposes a simple yet easy interface to WP Query, the posts API, post meta -API, users API, revisions API and many more. Chances are, if you can do it with -WordPress, WP API will let you do it. +WP API exposes a simple yet easy interface to WP Query, the posts API, post +meta API, users API, revisions API and many more. Chances are, if you can do +it with WordPress, WP API will let you do it. WP API also includes an easy-to-use JavaScript API based on Backbone models, allowing plugin and theme developers to get up and running without needing to @@ -42,34 +44,39 @@ Check out [our documentation][docs] for information on what's available in the API and how to use it. We've also got documentation on extending the API with extra data for plugin and theme developers! -There's no fixed timeline for integration into core at this time, but getting closer! +There's no fixed timeline for integration into core at this time, but getting +closer! ## Installation -Drop this directory into your plugins folder and activate it. You need to be using pretty permalinks -to use the plugin, as it uses custom rewrite rules to power the API. +Drop this directory into your plugins folder and activate it. You need to be +using pretty permalinks to use the plugin, as it uses custom rewrite rules to +power the API. -Also, be sure to use the Subversion `trunk` branch of WordPress Core as there are potentially recent commits to Core that the REST API relies on. See the [WordPress.org website](https://wordpress.org/download/svn/) for simple instructions. +Also, be sure to use the Subversion `trunk` branch of WordPress Core as there +are potentially recent commits to Core that the REST API relies on. See the +[WordPress.org website](https://wordpress.org/download/svn/) for simple instructions. ## Issue Tracking -All tickets for the project are being tracked on [GitHub][]. You can also take a -look at the [recent updates][] for the project. +All tickets for the project are being tracked on [GitHub][]. You can also take +a look at the [recent updates][] for the project. ## Contributing -Want to get involved? Check out [Contributing.md][contributing] for details on submitting fixes and new features. +Want to get involved? Check out [Contributing.md][contributing] for details on +submitting fixes and new features. ## Security We take the security of the API extremely seriously. If you think you've found a security issue with the API (whether information disclosure, privilege -escalation, or another issue), we'd appreciate responsible disclosure as soon as -possible. +escalation, or another issue), we'd appreciate responsible disclosure as soon +as possible. -To report a security issue, you can either email `security[at]wordpress.org`, or -[file an issue on HackerOne][hackerone]. We will attempt to give an initial +To report a security issue, you can either email `security[at]wordpress.org`, +or [file an issue on HackerOne][hackerone]. We will attempt to give an initial response to security issues within 48 hours at most, however keep in mind that the team is distributed across various timezones, and delays may occur as we discuss internally. From ce5d0b07925f14a7b5550ef211041fba6bd94c1b Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Wed, 1 Jun 2016 22:47:07 -0400 Subject: [PATCH 069/318] Fixing Schema Fixing schema of capabilities to match only object. --- lib/endpoints/class-wp-rest-users-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index beb4b12b71..50d979d314 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -826,13 +826,13 @@ public function get_item_schema() { ), 'capabilities' => array( 'description' => __( 'All capabilities assigned to the resource.' ), - 'type' => array( 'object', 'null' ), + 'type' => array( 'object' ), 'context' => array( 'edit' ), 'readonly' => true, ), 'extra_capabilities' => array( 'description' => __( 'Any extra capabilities assigned to the resource.' ), - 'type' => array( 'object', 'null' ), + 'type' => array( 'object' ), 'context' => array( 'edit' ), 'readonly' => true, ), From 6298c8d018d6b2e9d762959a51269cfbc418e31c Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Wed, 1 Jun 2016 23:04:17 -0400 Subject: [PATCH 070/318] Clean up - use type casting Cast caps as an object instead of leveraging array conversion. Saves effort on conditionals and makes for cleaner code. --- lib/endpoints/class-wp-rest-users-controller.php | 12 ++---------- tests/test-rest-users-controller.php | 12 ++---------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 50d979d314..b93a0c0dff 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -554,11 +554,11 @@ public function prepare_item_for_response( $user, $request ) { } if ( ! empty( $schema['properties']['capabilities'] ) ) { - $data['capabilities'] = $user->allcaps; + $data['capabilities'] = (object) $user->allcaps; } if ( ! empty( $schema['properties']['extra_capabilities'] ) ) { - $data['extra_capabilities'] = $user->caps; + $data['extra_capabilities'] = (object) $user->caps; } if ( ! empty( $schema['properties']['avatar_urls'] ) ) { @@ -567,14 +567,6 @@ public function prepare_item_for_response( $user, $request ) { $context = ! empty( $request['context'] ) ? $request['context'] : 'embed'; - if ( empty( $data['capabilities'] ) ) { - $data['capabilities'] = new stdClass(); - } - - if ( empty( $data['extra_capabilities'] ) ) { - $data['extra_capabilities'] = new stdClass(); - } - $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 6030031748..0da00844e2 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -1127,16 +1127,8 @@ protected function check_user_data( $user, $data, $context, $links ) { $this->assertEquals( $user->last_name, $data['last_name'] ); $this->assertEquals( $user->nickname, $data['nickname'] ); $this->assertEquals( $user->user_email, $data['email'] ); - if ( empty( $user->allcaps ) ) { - $this->assertEquals( new stdClass(), $data['capabilities'] ); - } else { - $this->assertEquals( $user->allcaps, $data['capabilities'] ); - } - if ( empty( $user->caps ) ) { - $this->assertEquals( new stdClass(), $data['extra_capabilities'] ); - } else { - $this->assertEquals( $user->caps, $data['extra_capabilities'] ); - } + $this->assertEquals( (object) $user->allcaps, $data['capabilities'] ); + $this->assertEquals( (object) $user->caps, $data['extra_capabilities'] ); $this->assertEquals( date( 'c', strtotime( $user->user_registered ) ), $data['registered_date'] ); $this->assertEquals( $user->user_login, $data['username'] ); $this->assertEquals( $user->roles, $data['roles'] ); From 14cd89ca3589f4ddf43e5a89be886d525c247baa Mon Sep 17 00:00:00 2001 From: Kevin Killingsworth Date: Wed, 8 Jun 2016 10:37:59 -0500 Subject: [PATCH 071/318] Update post status description for multiple posts. Multiple post status types can be used in a comma-delimited list within the string given for a post status query. This updates the description to reflect that ability. --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 3776c2376b..e5661d75d9 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1742,7 +1742,7 @@ public function get_collection_params() { ); $params['status'] = array( 'default' => 'publish', - 'description' => __( 'Limit result set to posts assigned a specific status.' ), + 'description' => __( 'Limit result set to posts assigned a specific status; can be comma-delimited list of status types.' ), 'sanitize_callback' => 'sanitize_key', 'type' => 'string', 'validate_callback' => array( $this, 'validate_user_can_query_private_statuses' ), From fb237b9d909c764355a717526063478caada22cd Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 8 Jun 2016 21:05:33 -0700 Subject: [PATCH 072/318] Introduce WP_REST_Controller::get_post() for calling get_post() and doing the_post action --- .../class-wp-rest-attachments-controller.php | 6 ++--- .../class-wp-rest-comments-controller.php | 12 +++++----- lib/endpoints/class-wp-rest-controller.php | 24 +++++++++++++++++++ .../class-wp-rest-posts-controller.php | 22 ++++++++--------- .../class-wp-rest-revisions-controller.php | 10 ++++---- tests/test-rest-controller.php | 21 ++++++++++++++++ 6 files changed, 70 insertions(+), 25 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index 38c86ba60f..c0ffb04e26 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -48,7 +48,7 @@ public function create_item_permissions_check( $request ) { // Attaching media to a post requires ability to edit said post if ( ! empty( $request['post'] ) ) { - $parent = get_post( (int) $request['post'] ); + $parent = $this->get_post( (int) $request['post'] ); $post_parent_type = get_post_type_object( $parent->post_type ); if ( ! current_user_can( $post_parent_type->cap->edit_post, $request['post'] ) ) { return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to upload media to this resource.' ), array( 'status' => rest_authorization_required_code() ) ); @@ -123,7 +123,7 @@ public function create_item( $request ) { } return $id; } - $attachment = get_post( $id ); + $attachment = $this->get_post( $id ); /** Include admin functions to get access to wp_generate_attachment_metadata() */ require_once ABSPATH . 'wp-admin/includes/admin.php'; @@ -177,7 +177,7 @@ public function update_item( $request ) { update_post_meta( $data['id'], '_wp_attachment_image_alt', $request['alt_text'] ); } - $attachment = get_post( $request['id'] ); + $attachment = $this->get_post( $request['id'] ); $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $attachment, $request ); $response = rest_ensure_response( $response ); diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index b0932ca8e9..0c0fefd6ce 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -71,7 +71,7 @@ public function get_items_permissions_check( $request ) { if ( ! empty( $request['post'] ) ) { foreach ( (array) $request['post'] as $post_id ) { - $post = get_post( $post_id ); + $post = $this->get_post( $post_id ); if ( ! empty( $post_id ) && $post && ! $this->check_read_post_permission( $post ) ) { return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } else if ( 0 === $post_id && ! current_user_can( 'moderate_comments' ) ) { @@ -230,7 +230,7 @@ public function get_item_permissions_check( $request ) { return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot read this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } - $post = get_post( $comment->comment_post_ID ); + $post = $this->get_post( $comment->comment_post_ID ); if ( $post && ! $this->check_read_post_permission( $post ) ) { return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); @@ -258,7 +258,7 @@ public function get_item( $request ) { } if ( ! empty( $comment->comment_post_ID ) ) { - $post = get_post( $comment->comment_post_ID ); + $post = $this->get_post( $comment->comment_post_ID ); if ( empty( $post ) ) { return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) ); } @@ -293,7 +293,7 @@ public function create_item_permissions_check( $request ) { return new WP_Error( 'rest_comment_invalid_status', __( 'Sorry, you cannot set status for comments.' ), array( 'status' => rest_authorization_required_code() ) ); } - if ( ! empty( $request['post'] ) && $post = get_post( (int) $request['post'] ) ) { + if ( ! empty( $request['post'] ) && $post = $this->get_post( (int) $request['post'] ) ) { if ( ! $this->check_read_post_permission( $post ) ) { return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); @@ -620,7 +620,7 @@ protected function prepare_links( $comment ) { } if ( 0 !== (int) $comment->comment_post_ID ) { - $post = get_post( $comment->comment_post_ID ); + $post = $this->get_post( $comment->comment_post_ID ); if ( ! empty( $post->ID ) ) { $obj = get_post_type_object( $post->post_type ); $base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name; @@ -1137,7 +1137,7 @@ protected function check_read_permission( $comment ) { return false; } - $post = get_post( $comment->comment_post_ID ); + $post = $this->get_post( $comment->comment_post_ID ); if ( $comment->comment_post_ID && $post ) { if ( ! $this->check_read_post_permission( $post ) ) { return false; diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 150f77d84c..d9615072b3 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -480,4 +480,28 @@ public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CRE return $endpoint_args; } + /** + * Retrieves post data given a post ID or post object. + * + * This is a subset of the functionality of the `get_post()` function, with + * the additional functionality of having `the_post` action done on the + * resultant post object. This is done so that plugins may manipulate the + * post that is used in the REST API. + * + * @see get_post() + * @global WP_Query $wp_query + * + * @param int|WP_Post $post Post ID or post object. Defaults to global $post. + * @return WP_Post|null A `WP_Post` object when successful. + */ + public function get_post( $post ) { + global $wp_query; + + $post = get_post( $post ); + if ( $post && $wp_query ) { + /** This action is documented in wp-includes/query.php */ + do_action_ref_array( 'the_post', array( &$post, &$wp_query ) ); + } + return $post; + } } diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index e5661d75d9..70db3304ea 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -212,7 +212,7 @@ public function get_items( $request ) { */ public function get_item_permissions_check( $request ) { - $post = get_post( (int) $request['id'] ); + $post = $this->get_post( (int) $request['id'] ); if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post' ), array( 'status' => rest_authorization_required_code() ) ); @@ -233,7 +233,7 @@ public function get_item_permissions_check( $request ) { */ public function get_item( $request ) { $id = (int) $request['id']; - $post = get_post( $id ); + $post = $this->get_post( $id ); if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) ); @@ -333,7 +333,7 @@ public function create_item( $request ) { return $terms_update; } - $post = get_post( $post_id ); + $post = $this->get_post( $post_id ); $this->update_additional_fields_for_object( $post, $request ); /** @@ -362,7 +362,7 @@ public function create_item( $request ) { */ public function update_item_permissions_check( $request ) { - $post = get_post( $request['id'] ); + $post = $this->get_post( $request['id'] ); $post_type = get_post_type_object( $this->post_type ); if ( $post && ! $this->check_update_permission( $post ) ) { @@ -392,7 +392,7 @@ public function update_item_permissions_check( $request ) { */ public function update_item( $request ) { $id = (int) $request['id']; - $post = get_post( $id ); + $post = $this->get_post( $id ); if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { return new WP_Error( 'rest_post_invalid_id', __( 'Post id is invalid.' ), array( 'status' => 400 ) ); @@ -440,7 +440,7 @@ public function update_item( $request ) { return $terms_update; } - $post = get_post( $post_id ); + $post = $this->get_post( $post_id ); $this->update_additional_fields_for_object( $post, $request ); /* This action is documented in lib/endpoints/class-wp-rest-controller.php */ @@ -459,7 +459,7 @@ public function update_item( $request ) { */ public function delete_item_permissions_check( $request ) { - $post = get_post( $request['id'] ); + $post = $this->get_post( $request['id'] ); if ( $post && ! $this->check_delete_permission( $post ) ) { return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete posts.' ), array( 'status' => rest_authorization_required_code() ) ); @@ -478,7 +478,7 @@ public function delete_item( $request ) { $id = (int) $request['id']; $force = (bool) $request['force']; - $post = get_post( $id ); + $post = $this->get_post( $id ); if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post id.' ), array( 'status' => 404 ) ); @@ -822,7 +822,7 @@ protected function prepare_item_for_database( $request ) { // Parent. if ( ! empty( $schema['properties']['parent'] ) && ! empty( $request['parent'] ) ) { - $parent = get_post( (int) $request['parent'] ); + $parent = $this->get_post( (int) $request['parent'] ); if ( empty( $parent ) ) { return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post parent id.' ), array( 'status' => 400 ) ); } @@ -921,7 +921,7 @@ protected function handle_featured_media( $featured_media, $post_id ) { * @param integer $post_id */ public function handle_template( $template, $post_id ) { - if ( in_array( $template, array_keys( wp_get_theme()->get_page_templates( get_post( $post_id ) ) ) ) ) { + if ( in_array( $template, array_keys( wp_get_theme()->get_page_templates( $this->get_post( $post_id ) ) ) ) ) { update_post_meta( $post_id, '_wp_page_template', $template ); } else { update_post_meta( $post_id, '_wp_page_template', '' ); @@ -999,7 +999,7 @@ public function check_read_permission( $post ) { // Can we read the parent if we're inheriting? if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) { - $parent = get_post( $post->post_parent ); + $parent = $this->get_post( $post->post_parent ); return $this->check_read_permission( $parent ); } diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index e4ada1c734..550d6ee89d 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -57,7 +57,7 @@ public function register_routes() { */ public function get_items_permissions_check( $request ) { - $parent = get_post( $request['parent'] ); + $parent = $this->get_post( $request['parent'] ); if ( ! $parent ) { return true; } @@ -77,7 +77,7 @@ public function get_items_permissions_check( $request ) { */ public function get_items( $request ) { - $parent = get_post( $request['parent'] ); + $parent = $this->get_post( $request['parent'] ); if ( ! $request['parent'] || ! $parent || $this->parent_post_type !== $parent->post_type ) { return new WP_Error( 'rest_post_invalid_parent', __( 'Invalid post parent id.' ), array( 'status' => 404 ) ); } @@ -110,12 +110,12 @@ public function get_item_permissions_check( $request ) { */ public function get_item( $request ) { - $parent = get_post( $request['parent'] ); + $parent = $this->get_post( $request['parent'] ); if ( ! $request['parent'] || ! $parent || $this->parent_post_type !== $parent->post_type ) { return new WP_Error( 'rest_post_invalid_parent', __( 'Invalid post parent id.' ), array( 'status' => 404 ) ); } - $revision = get_post( $request['id'] ); + $revision = $this->get_post( $request['id'] ); if ( ! $revision || 'revision' !== $revision->post_type ) { return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision id.' ), array( 'status' => 404 ) ); } @@ -137,7 +137,7 @@ public function delete_item_permissions_check( $request ) { return $response; } - $post = get_post( $request['id'] ); + $post = $this->get_post( $request['id'] ); if ( ! $post ) { return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision id.' ), array( 'status' => 404 ) ); } diff --git a/tests/test-rest-controller.php b/tests/test-rest-controller.php index 6131f6a3b4..aa62a4ab1e 100644 --- a/tests/test-rest-controller.php +++ b/tests/test-rest-controller.php @@ -134,4 +134,25 @@ public function test_get_endpoint_args_for_item_schema_default_value() { $this->assertEquals( 'a', $args['somedefault']['default'] ); } + + public function test_get_post() { + $post_id = $this->factory()->post->create( array( 'post_title' => 'Original' ) ); + $controller = new WP_REST_Test_Controller(); + + $action_count = did_action( 'the_post' ); + $post = $controller->get_post( $post_id ); + $this->assertEquals( 1 + $action_count, did_action( 'the_post' ) ); + $this->assertEquals( 'Original', $post->post_title ); + + add_action( 'the_post', array( $this, 'action_the_post_for_test_get_post' ), 10, 2 ); + $post = $controller->get_post( $post_id ); + $this->assertEquals( 'Overridden', $post->post_title ); + $this->assertEquals( 2 + $action_count, did_action( 'the_post' ) ); + } + + public function action_the_post_for_test_get_post( $post, $query ) { + $this->assertInstanceOf( 'WP_Post', $post ); + $this->assertInstanceOf( 'WP_Query', $query ); + $post->post_title = 'Overridden'; + } } From fb4edde0a72620df3186a8d0618b09f05f2906ef Mon Sep 17 00:00:00 2001 From: Luke Gedeon Date: Tue, 21 Jun 2016 11:42:52 -0400 Subject: [PATCH 073/318] The get_the_excerpt filter expects the post object as of WP 4.5. Pass it into prepare_excerpt_response() so that it can pass it on. --- lib/endpoints/class-wp-rest-posts-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index e5661d75d9..256837d55c 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -654,13 +654,13 @@ protected function get_allowed_query_vars() { * @param string $excerpt * @return string|null $excerpt */ - protected function prepare_excerpt_response( $excerpt ) { + protected function prepare_excerpt_response( $excerpt, $post ) { if ( post_password_required() ) { return __( 'There is no excerpt because this is a protected post.' ); } /** This filter is documented in wp-includes/post-template.php */ - $excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt ) ); + $excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt, $post ) ); if ( empty( $excerpt ) ) { return ''; @@ -1152,7 +1152,7 @@ public function prepare_item_for_response( $post, $request ) { if ( ! empty( $schema['properties']['excerpt'] ) ) { $data['excerpt'] = array( 'raw' => $post->post_excerpt, - 'rendered' => $this->prepare_excerpt_response( $post->post_excerpt ), + 'rendered' => $this->prepare_excerpt_response( $post->post_excerpt, $post ), ); } From f3fc6d51ba2b520887846fce305ce401896021ed Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 21 Jun 2016 21:55:35 +0200 Subject: [PATCH 074/318] Replace the_post action with new rest_the_post filter --- lib/endpoints/class-wp-rest-controller.php | 17 +++++++++++------ tests/test-rest-controller.php | 15 +++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index d9615072b3..aa1d8c2bc8 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -495,13 +495,18 @@ public function get_endpoint_args_for_item_schema( $method = WP_REST_Server::CRE * @return WP_Post|null A `WP_Post` object when successful. */ public function get_post( $post ) { - global $wp_query; + $post_obj = get_post( $post ); + + /** + * Filter the post. + * + * Allows plugins to filter the post object as returned by `\WP_REST_Controller::get_post()`. + * + * @param WP_Post|null $post_obj The post object as returned by `get_post()`. + * @param int|WP_Post $post The original value used to obtain the post object. + */ + $post = apply_filters( 'rest_the_post', $post_obj, $post ); - $post = get_post( $post ); - if ( $post && $wp_query ) { - /** This action is documented in wp-includes/query.php */ - do_action_ref_array( 'the_post', array( &$post, &$wp_query ) ); - } return $post; } } diff --git a/tests/test-rest-controller.php b/tests/test-rest-controller.php index aa62a4ab1e..7f0736573e 100644 --- a/tests/test-rest-controller.php +++ b/tests/test-rest-controller.php @@ -135,24 +135,27 @@ public function test_get_endpoint_args_for_item_schema_default_value() { $this->assertEquals( 'a', $args['somedefault']['default'] ); } + public $rest_the_post_filter_apply_count = 0; + public function test_get_post() { $post_id = $this->factory()->post->create( array( 'post_title' => 'Original' ) ); $controller = new WP_REST_Test_Controller(); - $action_count = did_action( 'the_post' ); $post = $controller->get_post( $post_id ); - $this->assertEquals( 1 + $action_count, did_action( 'the_post' ) ); $this->assertEquals( 'Original', $post->post_title ); - add_action( 'the_post', array( $this, 'action_the_post_for_test_get_post' ), 10, 2 ); + $filter_apply_count = $this->rest_the_post_filter_apply_count; + add_filter( 'rest_the_post', array( $this, 'filter_rest_the_post_for_test_get_post' ), 10, 2 ); $post = $controller->get_post( $post_id ); $this->assertEquals( 'Overridden', $post->post_title ); - $this->assertEquals( 2 + $action_count, did_action( 'the_post' ) ); + $this->assertEquals( 1 + $filter_apply_count, $this->rest_the_post_filter_apply_count ); } - public function action_the_post_for_test_get_post( $post, $query ) { + public function filter_rest_the_post_for_test_get_post( $post, $post_id ) { $this->assertInstanceOf( 'WP_Post', $post ); - $this->assertInstanceOf( 'WP_Query', $query ); + $this->assertInternalType( 'int', $post_id ); $post->post_title = 'Overridden'; + $this->rest_the_post_filter_apply_count += 1; + return $post; } } From c4d137fc796963d200cc865f7bd00da480c0a6b9 Mon Sep 17 00:00:00 2001 From: Daniel Bachhuber Date: Sun, 26 Jun 2016 12:27:04 +0200 Subject: [PATCH 075/318] Define type as a string, not an array --- lib/endpoints/class-wp-rest-users-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 7aff2bad42..84a1f3b555 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -818,13 +818,13 @@ public function get_item_schema() { ), 'capabilities' => array( 'description' => __( 'All capabilities assigned to the resource.' ), - 'type' => array( 'object' ), + 'type' => 'object', 'context' => array( 'edit' ), 'readonly' => true, ), 'extra_capabilities' => array( 'description' => __( 'Any extra capabilities assigned to the resource.' ), - 'type' => array( 'object' ), + 'type' => 'object', 'context' => array( 'edit' ), 'readonly' => true, ), From d7ea74c6e5d09c52825d2c9264a55be2072985be Mon Sep 17 00:00:00 2001 From: Doug Cone Date: Wed, 29 Jun 2016 20:33:33 -0400 Subject: [PATCH 076/318] change permissions check to match core --- lib/endpoints/class-wp-rest-attachments-controller.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index c0ffb04e26..1475a2a1fa 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -40,9 +40,8 @@ public function create_item_permissions_check( $request ) { return $ret; } - // "upload_files" cap is returned for an attachment by $post_type_obj->cap->create_posts $post_type_obj = get_post_type_object( $this->post_type ); - if ( ! current_user_can( $post_type_obj->cap->create_posts ) || ! current_user_can( $post_type_obj->cap->edit_posts ) ) { + if ( ! current_user_can( 'upload_files' ) ) { return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to upload media on this site.' ), array( 'status' => 400 ) ); } From c5ec6da10a8db17684c71c4eff9b15652fd4d7dd Mon Sep 17 00:00:00 2001 From: Doug Cone Date: Thu, 30 Jun 2016 13:47:52 -0400 Subject: [PATCH 077/318] add role with upload_files cap, add tests for role extended the setup function to create a custom role with 'upload_files' permission and created two tests which check if a user with upload_files can create attachments, and also if a user with upload attachments but no edit permissions is rejected when attempting to add an attachment to a post they do not have permissions on. --- tests/test-rest-attachments-controller.php | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/test-rest-attachments-controller.php b/tests/test-rest-attachments-controller.php index f490769a16..cedeb52827 100644 --- a/tests/test-rest-attachments-controller.php +++ b/tests/test-rest-attachments-controller.php @@ -20,6 +20,17 @@ public function setUp() { $this->contributor_id = $this->factory->user->create( array( 'role' => 'contributor', ) ); + //matches core capabilities for subscriber role and adds upload_files + _x('File upload role', 'User role'); + add_role('uploader', 'File upload role'); + $role = get_role('uploader'); + $role->add_cap('upload_files'); + $role->add_cap('read'); + $role->add_cap('level_0'); + //print_r(array('role'=>$role)); + $this->uploader_id = $this->factory->user->create( array( + 'role' => 'uploader', + ) ); $orig_file = dirname( __FILE__ ) . '/data/canola.jpg'; $this->test_file = '/tmp/canola.jpg'; @@ -450,6 +461,22 @@ public function test_create_item_with_files() { $this->assertEquals( 201, $response->get_status() ); } + public function test_create_item_with_upload_files_role() { + wp_set_current_user( $this->uploader_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/media' ); + $request->set_file_params( array( + 'file' => array( + 'file' => file_get_contents( $this->test_file ), + 'name' => 'canola.jpg', + 'size' => filesize( $this->test_file ), + 'tmp_name' => $this->test_file, + ), + ) ); + $request->set_header( 'Content-MD5', md5_file( $this->test_file ) ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 201, $response->get_status() ); + } + public function test_create_item_empty_body() { wp_set_current_user( $this->author_id ); $request = new WP_REST_Request( 'POST', '/wp/v2/media' ); @@ -517,6 +544,15 @@ public function test_create_item_invalid_edit_permissions() { $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 ); } + public function test_create_item_invalid_upload_permissions() { + $post_id = $this->factory->post->create( array( 'post_author' => $this->editor_id ) ); + wp_set_current_user( $this->uploader_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/media' ); + $request->set_param( 'post', $post_id ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 ); + } + public function test_create_item_invalid_post_type() { $attachment_id = $this->factory->post->create( array( 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_parent' => 0 ) ); wp_set_current_user( $this->editor_id ); From d3c362b55cc84892cbceeec3f0010b0b188ad218 Mon Sep 17 00:00:00 2001 From: Doug Cone Date: Thu, 30 Jun 2016 14:12:56 -0400 Subject: [PATCH 078/318] fix to match coding standards --- tests/test-rest-attachments-controller.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test-rest-attachments-controller.php b/tests/test-rest-attachments-controller.php index cedeb52827..bea5e6f52a 100644 --- a/tests/test-rest-attachments-controller.php +++ b/tests/test-rest-attachments-controller.php @@ -21,12 +21,12 @@ public function setUp() { 'role' => 'contributor', ) ); //matches core capabilities for subscriber role and adds upload_files - _x('File upload role', 'User role'); - add_role('uploader', 'File upload role'); - $role = get_role('uploader'); - $role->add_cap('upload_files'); - $role->add_cap('read'); - $role->add_cap('level_0'); + _x( 'File upload role', 'User role' ); + add_role( 'uploader', 'File upload role' ); + $role = get_role( 'uploader' ); + $role->add_cap( 'upload_files' ); + $role->add_cap( 'read' ); + $role->add_cap( 'level_0' ); //print_r(array('role'=>$role)); $this->uploader_id = $this->factory->user->create( array( 'role' => 'uploader', From 54d0acdd3bcc18c783318add5c0a6f3fbb75c300 Mon Sep 17 00:00:00 2001 From: websupporter Date: Sat, 2 Jul 2016 10:50:06 +0000 Subject: [PATCH 079/318] adding relevance orderby --- lib/endpoints/class-wp-rest-posts-controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 831afb12c1..103580df69 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1708,6 +1708,7 @@ public function get_collection_params() { 'default' => 'date', 'enum' => array( 'date', + 'relevance', 'id', 'include', 'title', From ef18f250943521accda141b605b6c0630b596b54 Mon Sep 17 00:00:00 2001 From: websupporter Date: Sat, 2 Jul 2016 11:05:12 +0000 Subject: [PATCH 080/318] adding relevance orderby test --- tests/test-rest-posts-controller.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index f5bf647c1f..7f057dd811 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -270,6 +270,15 @@ public function test_get_items_order_and_orderby() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $this->assertEquals( 'Apple Cobbler', $data[0]['title']['rendered'] ); + + //Relevance orderby + $this->factory->post->create( array( 'post_title' => 'Coffee Cake is relevant', 'post_content' => 'Apple', 'post_status' => 'publish' ) ); + $this->factory->post->create( array( 'post_title' => 'Coffee Cake', 'post_content' => 'Apple is less relevant', 'post_status' => 'publish' ) ); + $request->set_param( 'search', 'relevant' ); + $request->set_param( 'orderby', 'relevance' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 'Coffee Cake is relevant', $data[0]['title']['rendered'] ); } public function test_get_items_ignore_sticky_posts_by_default() { From 5442bf90cb38364048673f10fd917ec128f89ec6 Mon Sep 17 00:00:00 2001 From: websupporter Date: Mon, 25 Jul 2016 12:17:40 +0300 Subject: [PATCH 081/318] Add permission check whether ['search'] is present --- lib/endpoints/class-wp-rest-posts-controller.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 103580df69..ede86c6f11 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -71,6 +71,10 @@ public function get_items_permissions_check( $request ) { $post_type = get_post_type_object( $this->post_type ); + if ( ! empty( $request['orderby'] ) && $request['orderby'] === 'relevance' && empty( $request['search'] ) ) { + return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term in order to use the relevance search.' ), array( 'status' => 400 ) ); + } + if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); } From ae500cbbfe7bb964f6fb2094fb382b0466e26f29 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Mon, 25 Jul 2016 22:40:25 -0500 Subject: [PATCH 082/318] Typecast the user_id in search to a string to allow WP_User_Query to use the correct search_column for a match --- tests/test-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 0da00844e2..0c24bb7838 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -327,7 +327,7 @@ public function test_get_items_search() { $this->assertEquals( 0, count( $response->get_data() ) ); $yolo_id = $this->factory->user->create( array( 'display_name' => 'yololololo' ) ); $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); - $request->set_param( 'search', $yolo_id ); + $request->set_param( 'search', (string) $yolo_id ); $response = $this->server->dispatch( $request ); $this->assertEquals( 1, count( $response->get_data() ) ); // default to wildcard search From a490307478a2d6c31412c065cb0f46ed17f679eb Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Fri, 29 Jul 2016 13:50:47 -0400 Subject: [PATCH 083/318] Fix #2587 - Fix registered date schema for users Fixes #2587 - Changes the registered date field in the users endpoint to the proper schema. --- lib/endpoints/class-wp-rest-users-controller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 84a1f3b555..5048f50e3c 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -801,7 +801,8 @@ public function get_item_schema() { ), 'registered_date' => array( 'description' => __( 'Registration date for the resource.' ), - 'type' => 'date-time', + 'type' => 'string', + 'format' => 'date-time', 'context' => array( 'edit' ), 'readonly' => true, ), From 565b9a71390d6c86c63d656172b24aa263119418 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sun, 31 Jul 2016 14:22:48 -0500 Subject: [PATCH 084/318] Revert status back to 400 where slug or featured image parameter is invalid --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 0a4378c7ad..6a748e0f14 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -906,7 +906,7 @@ protected function handle_featured_media( $featured_media, $post_id ) { if ( $result ) { return true; } else { - return new WP_Error( 'rest_invalid_featured_media', __( 'Invalid featured media id.' ), array( 'status' => 404 ) ); + return new WP_Error( 'rest_invalid_featured_media', __( 'Invalid featured media id.' ), array( 'status' => 400 ) ); } } else { return delete_post_thumbnail( $post_id ); diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index e0012938b5..d6dc78199c 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -380,7 +380,7 @@ public function update_item( $request ) { } if ( ! empty( $request['slug'] ) && $request['slug'] !== $user->user_nicename && get_user_by( 'slug', $request['slug'] ) ) { - return new WP_Error( 'rest_user_invalid_slug', __( 'Slug is invalid.' ), array( 'status' => 404 ) ); + return new WP_Error( 'rest_user_invalid_slug', __( 'Slug is invalid.' ), array( 'status' => 400 ) ); } if ( ! empty( $request['roles'] ) ) { From 940bfa3808926d97a793891f224208e5dd08d138 Mon Sep 17 00:00:00 2001 From: Thorsten Frommen Date: Fri, 5 Aug 2016 07:25:17 +0200 Subject: [PATCH 085/318] Improve WP_REST_Controller::filter_response_by_context(). --- lib/endpoints/class-wp-rest-controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index aa1d8c2bc8..d348f0ab42 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -189,6 +189,7 @@ public function filter_response_by_context( $data, $context ) { if ( ! in_array( $context, $schema['properties'][ $key ]['context'] ) ) { unset( $data[ $key ] ); + continue; } if ( 'object' === $schema['properties'][ $key ]['type'] && ! empty( $schema['properties'][ $key ]['properties'] ) ) { From 2a2b4eac56022d06380181c943c28092de5b48c2 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Sun, 7 Aug 2016 13:53:23 -0400 Subject: [PATCH 086/318] Document options of the "status" parameter for Post collection GETs --- lib/endpoints/class-wp-rest-posts-controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index a0cec86c48..35e8062caf 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1743,6 +1743,7 @@ public function get_collection_params() { $params['status'] = array( 'default' => 'publish', 'description' => __( 'Limit result set to posts assigned a specific status; can be comma-delimited list of status types.' ), + 'enum' => array_merge( array_keys( get_post_stati() ), array( 'any' ) ), 'sanitize_callback' => 'sanitize_key', 'type' => 'string', 'validate_callback' => array( $this, 'validate_user_can_query_private_statuses' ), From 359b8b9b8339e45824c2b99093c5abd5655992cc Mon Sep 17 00:00:00 2001 From: websupporter Date: Fri, 12 Aug 2016 13:08:19 +0300 Subject: [PATCH 087/318] set string before variable in if-statement --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index ede86c6f11..9a12016067 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -71,7 +71,7 @@ public function get_items_permissions_check( $request ) { $post_type = get_post_type_object( $this->post_type ); - if ( ! empty( $request['orderby'] ) && $request['orderby'] === 'relevance' && empty( $request['search'] ) ) { + if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) { return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term in order to use the relevance search.' ), array( 'status' => 400 ) ); } From d0a4b2181ce32cd1df8054a835e100a9704edac7 Mon Sep 17 00:00:00 2001 From: "Toro_Unit (Hiroshi Urabe)" Date: Wed, 17 Aug 2016 11:25:52 +0900 Subject: [PATCH 088/318] fix forum url and installer-name --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 6a85df3fe7..685dbfd2f5 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,7 @@ ], "support": { "issues": "https://github.com/WP-API/WP-API/issues", - "forum": "https://wordpress.org/support/plugin/json-rest-api" + "forum": "https://wordpress.org/support/plugin/rest-api" }, "require": { "composer/installers": "~1.0" @@ -22,7 +22,7 @@ "wp-coding-standards/wpcs": "0.6.0" }, "extra": { - "installer-name": "json-rest-api" + "installer-name": "rest-api" }, "scripts": { "post-install-cmd": "\"vendor/bin/phpcs\" --config-set installed_paths vendor/wp-coding-standards/wpcs", From 417b29066aaccc2017a1616cb8d931be2c090ef2 Mon Sep 17 00:00:00 2001 From: websupporter Date: Wed, 17 Aug 2016 22:00:59 +0300 Subject: [PATCH 089/318] move check to get_items() --- lib/endpoints/class-wp-rest-posts-controller.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 9a12016067..5839771a6c 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -71,10 +71,6 @@ public function get_items_permissions_check( $request ) { $post_type = get_post_type_object( $this->post_type ); - if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) { - return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term in order to use the relevance search.' ), array( 'status' => 400 ) ); - } - if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -89,6 +85,12 @@ public function get_items_permissions_check( $request ) { * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { + + //Make sure a search string is set in case the orderby is set to 'relevace' + if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) { + return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term in order to use the relevance search.' ), array( 'status' => 400 ) ); + } + $args = array(); $args['author__in'] = $request['author']; $args['author__not_in'] = $request['author_exclude']; From 39cc02150dbbbc050aa53915c9509d5be451a0d5 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Sat, 20 Aug 2016 14:01:21 -0400 Subject: [PATCH 090/318] Fixes #1612 by adding link to collection sharing comment parent. Fixes #1612, This patch adds a `children` link to the response when the comment has children. The link is to a collection of comments who share the same parent as the original comment resource. This enables a client to then recursively move through the links, with pagination parameters to boot, to performantly collect all threaded comments of a single comment. --- .../class-wp-rest-comments-controller.php | 12 +++ tests/test-rest-comments-controller.php | 92 +++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index f3971bb463..31eae84aed 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -640,6 +640,18 @@ protected function prepare_links( $comment ) { ); } + // Only grab one comment to verify the comment has children. + $comment_children = $comment->get_children( array( 'number' => 1 ) ); + if ( ! empty( $comment_children ) ) { + $args = array( 'parent' => $comment->comment_ID ); + $rest_url = add_query_arg( $args, rest_url( $this->namespace . $this->rest_base ) ); + + $links['children'] = array( + 'href' => $rest_url, + 'embeddable' => true, + ); + } + return $links; } diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 583da7e91c..3f2013a804 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -573,6 +573,39 @@ public function test_get_comment_not_approved_same_user() { $this->assertEquals( 200, $response->get_status() ); } + public function test_get_comment_with_children_link() { + $comment_id_1 = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->post_id, + 'user_id' => $this->subscriber_id, + ) ); + + $child_comment = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_parent' => $comment_id_1, + 'comment_post_ID' => $this->post_id, + 'user_id' => $this->subscriber_id, + ) ); + + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', $comment_id_1 ) ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $this->assertArrayHasKey( 'children', $response->get_links() ); + } + + public function test_get_comment_without_children_link() { + $comment_id_1 = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->post_id, + 'user_id' => $this->subscriber_id, + ) ); + + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', $comment_id_1 ) ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $this->assertArrayNotHasKey( 'children', $response->get_links() ); + } + public function test_create_item() { wp_set_current_user( 0 ); @@ -1136,6 +1169,39 @@ public function test_update_comment_invalid_permission() { $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); } + public function test_update_comment_with_children_link() { + wp_set_current_user( $this->admin_id ); + $comment_id_1 = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->post_id, + 'user_id' => $this->subscriber_id, + ) ); + + $child_comment = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->post_id, + 'user_id' => $this->subscriber_id, + ) ); + + // Check if comment 1 does not have the child link. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', $comment_id_1 ) ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $this->assertArrayNotHasKey( 'children', $response->get_links() ); + + // Change the comment parent. + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%s', $child_comment ) ); + $request->set_param( 'parent', $comment_id_1 ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + // Check if comment 1 now has the child link. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', $comment_id_1 ) ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $this->assertArrayHasKey( 'children', $response->get_links() ); + } + public function test_delete_item() { wp_set_current_user( $this->admin_id ); @@ -1203,6 +1269,32 @@ public function test_delete_comment_without_permission() { $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); } + public function test_delete_child_comment_link() { + wp_set_current_user( $this->admin_id ); + $comment_id_1 = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->post_id, + 'user_id' => $this->subscriber_id, + ) ); + + $child_comment = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_parent' => $comment_id_1, + 'comment_post_ID' => $this->post_id, + 'user_id' => $this->subscriber_id, + ) ); + + $request = new WP_REST_Request( 'DELETE', sprintf( '/wp/v2/comments/%s', $child_comment ) ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + // Verify children link is gone. + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', $comment_id_1 ) ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $this->assertArrayNotHasKey( 'children', $response->get_links() ); + } + public function test_get_item_schema() { $request = new WP_REST_Request( 'OPTIONS', '/wp/v2/comments' ); $response = $this->server->dispatch( $request ); From d35f94b0c6c49bbeea19b8ee79cd2f0752eaa335 Mon Sep 17 00:00:00 2001 From: websupporter Date: Sun, 21 Aug 2016 22:16:29 +0300 Subject: [PATCH 091/318] add filter[s] to allow for relevance search --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 78c23d7d35..584306d695 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -87,7 +87,7 @@ public function get_items_permissions_check( $request ) { public function get_items( $request ) { //Make sure a search string is set in case the orderby is set to 'relevace' - if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) ) { + if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) && empty( $request['filter']['s'] ) ) { return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term in order to use the relevance search.' ), array( 'status' => 400 ) ); } From 0b42ded95b1f6f944ceec64483ffbb2a9a5b0b15 Mon Sep 17 00:00:00 2001 From: websupporter Date: Sun, 21 Aug 2016 23:19:24 +0300 Subject: [PATCH 092/318] Add test case --- tests/test-rest-posts-controller.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 85d55901ce..aca9b72bc7 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -279,6 +279,13 @@ public function test_get_items_order_and_orderby() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $this->assertEquals( 'Coffee Cake is relevant', $data[0]['title']['rendered'] ); + + //Test relevance with filter[s] parameter + $request->set_param( 'filter', array( 's' => 'relevant' ) ); + $request->set_param( 'orderby', 'relevance' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 'Coffee Cake is relevant', $data[0]['title']['rendered'] ); } public function test_get_items_ignore_sticky_posts_by_default() { From 3cae39f2079c6ef41ffc7a6ceafba2fe39e8dcf6 Mon Sep 17 00:00:00 2001 From: websupporter Date: Mon, 22 Aug 2016 09:36:47 +0300 Subject: [PATCH 093/318] relevance test function + new error msg --- .../class-wp-rest-posts-controller.php | 2 +- tests/test-rest-posts-controller.php | 18 ++++++------------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 584306d695..ca2e98e9a5 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -88,7 +88,7 @@ public function get_items( $request ) { //Make sure a search string is set in case the orderby is set to 'relevace' if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) && empty( $request['filter']['s'] ) ) { - return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term in order to use the relevance search.' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) ); } $args = array(); diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index aca9b72bc7..be39683cf2 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -270,22 +270,16 @@ public function test_get_items_order_and_orderby() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $this->assertEquals( 'Apple Cobbler', $data[0]['title']['rendered'] ); + } - //Relevance orderby - $this->factory->post->create( array( 'post_title' => 'Coffee Cake is relevant', 'post_content' => 'Apple', 'post_status' => 'publish' ) ); - $this->factory->post->create( array( 'post_title' => 'Coffee Cake', 'post_content' => 'Apple is less relevant', 'post_status' => 'publish' ) ); - $request->set_param( 'search', 'relevant' ); - $request->set_param( 'orderby', 'relevance' ); - $response = $this->server->dispatch( $request ); - $data = $response->get_data(); - $this->assertEquals( 'Coffee Cake is relevant', $data[0]['title']['rendered'] ); + public function test_get_items_with_orderby_relevance() { + $this->factory->post->create( array( 'post_title' => 'Title is more relevant', 'post_content' => 'Content is', 'post_status' => 'publish' ) ); + $this->factory->post->create( array( 'post_title' => 'Title is', 'post_content' => 'Content is less relevant', 'post_status' => 'publish' ) ); - //Test relevance with filter[s] parameter - $request->set_param( 'filter', array( 's' => 'relevant' ) ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $request->set_param( 'orderby', 'relevance' ); $response = $this->server->dispatch( $request ); - $data = $response->get_data(); - $this->assertEquals( 'Coffee Cake is relevant', $data[0]['title']['rendered'] ); + $this->assertErrorResponse( 'rest_no_search_term_defined', $response, 400 ); } public function test_get_items_ignore_sticky_posts_by_default() { From b4ef5e578022044e15544efd7489b5c26187b8c8 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 6 Sep 2016 11:42:15 -0400 Subject: [PATCH 094/318] Alter default comment sort order to be "desc" This follows discussion in #2588 and [in slack][slack-log] around standardizing the sort order of the comments endpoint to bring it in line with both the other endpoints in the API, and with `WP_Comment_Query`, by switching back to "desc" as the default sort order. Note that is a breaking change if API consumers are relying on comment order to be ascending by default. Closes #2588 [slack-log]: https://wordpress.slack.com/archives/core-restapi/p1473088762000227 --- .../class-wp-rest-comments-controller.php | 2 +- tests/test-rest-comments-controller.php | 24 ++++++++++++++++++- tests/test-rest-posts-controller.php | 2 +- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index f3971bb463..3064f7b45e 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -1006,7 +1006,7 @@ public function get_collection_params() { 'type' => 'string', 'sanitize_callback' => 'sanitize_key', 'validate_callback' => 'rest_validate_request_arg', - 'default' => 'asc', + 'default' => 'desc', 'enum' => array( 'asc', 'desc', diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 583da7e91c..314586a7d6 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -179,7 +179,8 @@ public function test_get_items_include_query() { $this->factory->comment->create( $args ); $id3 = $this->factory->comment->create( $args ); $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); - // Orderby=>desc + // Order=>asc + $request->set_param( 'order', 'asc' ); $request->set_param( 'include', array( $id3, $id1 ) ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); @@ -236,6 +237,27 @@ public function test_get_items_offset_query() { $this->assertCount( 2, $response->get_data() ); } + public function test_get_items_order_query() { + wp_set_current_user( $this->admin_id ); + $args = array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->post_id, + ); + $this->factory->comment->create( $args ); + $this->factory->comment->create( $args ); + $id3 = $this->factory->comment->create( $args ); + $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); + // order defaults to 'desc' + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( $id3, $data[0]['id'] ); + // order=>asc + $request->set_param( 'order', 'asc' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( $this->approved_id, $data[0]['id'] ); + } + public function test_get_items_private_post_no_permissions() { wp_set_current_user( 0 ); $post_id = $this->factory->post->create( array( 'post_status' => 'private' ) ); diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index be39683cf2..03bd834e95 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -265,7 +265,7 @@ public function test_get_items_order_and_orderby() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $this->assertEquals( 'Apple Sauce', $data[0]['title']['rendered'] ); - // order=>desc + // order=>asc $request->set_param( 'order', 'asc' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); From fc698de4041b07e8ab793ae2e499b71d06f1a25d Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 13 Sep 2016 09:43:22 +0300 Subject: [PATCH 095/318] extent revisions to show raw + rendered --- .../class-wp-rest-revisions-controller.php | 145 ++++++++++++------ 1 file changed, 97 insertions(+), 48 deletions(-) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index 734b0a5da4..c4ef6d9fb5 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -180,31 +180,80 @@ public function delete_item( $request ) { */ public function prepare_item_for_response( $post, $request ) { - // Base fields for every post - $data = array( - 'author' => $post->post_author, - 'date' => $this->prepare_date_response( $post->post_date_gmt, $post->post_date ), - 'date_gmt' => $this->prepare_date_response( $post->post_date_gmt ), - 'guid' => $post->guid, - 'id' => $post->ID, - 'modified' => $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified ), - 'modified_gmt' => $this->prepare_date_response( $post->post_modified_gmt ), - 'parent' => (int) $post->post_parent, - 'slug' => $post->post_name, - ); - $schema = $this->get_item_schema(); + $data = array(); + + if ( ! empty( $schema['properties']['author'] ) ) { + $data['author'] = $post->post_author; + } + + if ( ! empty( $schema['properties']['date'] ) ) { + $data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date ); + } + + if ( ! empty( $schema['properties']['date_gmt'] ) ) { + $data['date'] = $this->prepare_date_response( $post->post_date_gmt ); + } + + if ( ! empty( $schema['properties']['id'] ) ) { + $data['date'] = $post->ID; + } + + if ( ! empty( $schema['properties']['modified'] ) ) { + $data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified ); + } + + if ( ! empty( $schema['properties']['modified_gmt'] ) ) { + $data['modified_gmt'] = $this->prepare_date_response( $post->post_modified_gmt ); + } + + if ( ! empty( $schema['properties']['parent'] ) ) { + $data['parent'] = (int) $post->post_parent; + } + + if ( ! empty( $schema['properties']['parent'] ) ) { + $data['slug'] = $post->post_name; + } + + if ( ! empty( $schema['properties']['guid'] ) ) { + $data['guid'] = array( + /** This filter is documented in wp-includes/post-template.php */ + 'rendered' => apply_filters( 'get_the_guid', $post->guid ), + 'raw' => $post->guid, + ); + } + if ( ! empty( $schema['properties']['title'] ) ) { - $data['title'] = $post->post_title; + $data['title'] = array( + 'raw' => $post->post_title, + 'rendered' => get_the_title( $post->ID ), + ); } if ( ! empty( $schema['properties']['content'] ) ) { - $data['content'] = $post->post_content; + + if ( ! empty( $post->post_password ) ) { + $this->prepare_password_response( $post->post_password ); + } + + $data['content'] = array( + 'raw' => $post->post_content, + /** This filter is documented in wp-includes/post-template.php */ + 'rendered' => apply_filters( 'the_content', $post->post_content ), + ); + + // Don't leave our cookie lying around: https://github.com/WP-API/WP-API/issues/1055. + if ( ! empty( $post->post_password ) ) { + $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = ''; + } } if ( ! empty( $schema['properties']['excerpt'] ) ) { - $data['excerpt'] = $post->post_excerpt; + $data['excerpt'] = array( + 'raw' => $post->post_excerpt, + 'rendered' => $this->prepare_excerpt_response( $post->post_excerpt, $post ), + ); } $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; @@ -316,38 +365,17 @@ public function get_item_schema() { $parent_schema = $this->parent_controller->get_item_schema(); - foreach ( array( 'title', 'content', 'excerpt' ) as $property ) { - if ( empty( $parent_schema['properties'][ $property ] ) ) { - continue; - } - - switch ( $property ) { - - case 'title': - $schema['properties']['title'] = array( - 'description' => __( 'Title for the object, as it exists in the database.' ), - 'type' => 'string', - 'context' => array( 'view', 'edit', 'embed' ), - ); - break; - - case 'content': - $schema['properties']['content'] = array( - 'description' => __( 'Content for the object, as it exists in the database.' ), - 'type' => 'string', - 'context' => array( 'view', 'edit' ), - ); - break; - - case 'excerpt': - $schema['properties']['excerpt'] = array( - 'description' => __( 'Excerpt for the object, as it exists in the database.' ), - 'type' => 'string', - 'context' => array( 'view', 'edit', 'embed' ), - ); - break; - - } + if ( ! empty( $parent_schema['properties']['title'] ) ) { + $schema['properties']['title'] = $parent_schema['properties']['title']; + } + if ( ! empty( $parent_schema['properties']['content'] ) ) { + $schema['properties']['content'] = $parent_schema['properties']['content']; + } + if ( ! empty( $parent_schema['properties']['excerpt'] ) ) { + $schema['properties']['excerpt'] = $parent_schema['properties']['excerpt']; + } + if ( ! empty( $parent_schema['properties']['guid'] ) ) { + $schema['properties']['guid'] = $parent_schema['properties']['guid']; } return $this->add_additional_fields_schema( $schema ); @@ -364,4 +392,25 @@ public function get_collection_params() { ); } + /** + * Check the post excerpt and prepare it for single post output. + * + * @param string $excerpt + * @return string|null $excerpt + */ + protected function prepare_excerpt_response( $excerpt, $post ) { + if ( post_password_required() ) { + return __( 'There is no excerpt because this is a protected post.' ); + } + + /** This filter is documented in wp-includes/post-template.php */ + $excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt, $post ) ); + + if ( empty( $excerpt ) ) { + return ''; + } + + return $excerpt; + } + } From 1c26a4f60e969ebe55fc661964578f0cb95d78be Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 13 Sep 2016 11:14:07 +0300 Subject: [PATCH 096/318] revision bugfix + unit test adjusting --- .../class-wp-rest-revisions-controller.php | 6 +++--- tests/test-rest-revisions-controller.php | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index c4ef6d9fb5..71fe366054 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -193,11 +193,11 @@ public function prepare_item_for_response( $post, $request ) { } if ( ! empty( $schema['properties']['date_gmt'] ) ) { - $data['date'] = $this->prepare_date_response( $post->post_date_gmt ); + $data['date_gmt'] = $this->prepare_date_response( $post->post_date_gmt ); } if ( ! empty( $schema['properties']['id'] ) ) { - $data['date'] = $post->ID; + $data['id'] = $post->ID; } if ( ! empty( $schema['properties']['modified'] ) ) { @@ -212,7 +212,7 @@ public function prepare_item_for_response( $post, $request ) { $data['parent'] = (int) $post->post_parent; } - if ( ! empty( $schema['properties']['parent'] ) ) { + if ( ! empty( $schema['properties']['slug'] ) ) { $data['slug'] = $post->post_name; } diff --git a/tests/test-rest-revisions-controller.php b/tests/test-rest-revisions-controller.php index 73b7b60b44..13c6a85308 100644 --- a/tests/test-rest-revisions-controller.php +++ b/tests/test-rest-revisions-controller.php @@ -265,18 +265,28 @@ protected function check_get_revision_response( $response, $revision ) { $this->assertArrayHasKey( '_links', $response ); $links = $response['_links']; } - + $this->assertEquals( $revision->post_author, $response['author'] ); - $this->assertEquals( $revision->post_content, $response['content'] ); + + $rendered_content = apply_filters( 'the_content', $revision->post_content ); + $this->assertEquals( $rendered_content, $response['content']['rendered'] ); + $this->assertEquals( mysql_to_rfc3339( $revision->post_date ), $response['date'] ); $this->assertEquals( mysql_to_rfc3339( $revision->post_date_gmt ), $response['date_gmt'] ); - $this->assertEquals( $revision->post_excerpt, $response['excerpt'] ); - $this->assertEquals( $revision->guid, $response['guid'] ); + + $rendered_excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $revision->post_excerpt, $revision ) ); + $this->assertEquals( $rendered_excerpt, $response['excerpt']['rendered'] ); + + $rendered_guid = apply_filters( 'get_the_guid', $revision->guid ); + $this->assertEquals( $rendered_guid, $response['guid']['rendered'] ); + $this->assertEquals( $revision->ID, $response['id'] ); $this->assertEquals( mysql_to_rfc3339( $revision->post_modified ), $response['modified'] ); $this->assertEquals( mysql_to_rfc3339( $revision->post_modified_gmt ), $response['modified_gmt'] ); $this->assertEquals( $revision->post_name, $response['slug'] ); - $this->assertEquals( $revision->post_title, $response['title'] ); + + $rendered_title = get_the_title( $revision->ID ); + $this->assertEquals( $rendered_title, $response['title']['rendered'] ); $parent = get_post( $revision->post_parent ); $parent_controller = new WP_REST_Posts_Controller( $parent->post_type ); From ee69cce6cbf62546deae0034d4af0a29d5d49f80 Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 13 Sep 2016 11:27:55 +0300 Subject: [PATCH 097/318] remove whitespaces --- .../class-wp-rest-revisions-controller.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index 71fe366054..e012818907 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -187,31 +187,31 @@ public function prepare_item_for_response( $post, $request ) { if ( ! empty( $schema['properties']['author'] ) ) { $data['author'] = $post->post_author; } - + if ( ! empty( $schema['properties']['date'] ) ) { $data['date'] = $this->prepare_date_response( $post->post_date_gmt, $post->post_date ); } - + if ( ! empty( $schema['properties']['date_gmt'] ) ) { $data['date_gmt'] = $this->prepare_date_response( $post->post_date_gmt ); } - + if ( ! empty( $schema['properties']['id'] ) ) { $data['id'] = $post->ID; } - + if ( ! empty( $schema['properties']['modified'] ) ) { $data['modified'] = $this->prepare_date_response( $post->post_modified_gmt, $post->post_modified ); } - + if ( ! empty( $schema['properties']['modified_gmt'] ) ) { $data['modified_gmt'] = $this->prepare_date_response( $post->post_modified_gmt ); } - + if ( ! empty( $schema['properties']['parent'] ) ) { $data['parent'] = (int) $post->post_parent; } - + if ( ! empty( $schema['properties']['slug'] ) ) { $data['slug'] = $post->post_name; } From f338cf3d0a7c6d388421774abc3561eec8914e52 Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 13 Sep 2016 11:33:29 +0300 Subject: [PATCH 098/318] remove whitespaces in test --- tests/test-rest-revisions-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-revisions-controller.php b/tests/test-rest-revisions-controller.php index 13c6a85308..ff453a3cc2 100644 --- a/tests/test-rest-revisions-controller.php +++ b/tests/test-rest-revisions-controller.php @@ -265,7 +265,7 @@ protected function check_get_revision_response( $response, $revision ) { $this->assertArrayHasKey( '_links', $response ); $links = $response['_links']; } - + $this->assertEquals( $revision->post_author, $response['author'] ); $rendered_content = apply_filters( 'the_content', $revision->post_content ); From b5065957f5eb8b12b8fa2b9874eedd911d8996b8 Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 13 Sep 2016 11:51:05 +0300 Subject: [PATCH 099/318] add password prepare function --- .../class-wp-rest-revisions-controller.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index e012818907..9e1df4009c 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -413,4 +413,19 @@ protected function prepare_excerpt_response( $excerpt, $post ) { return $excerpt; } + protected function prepare_password_response( $password ) { + if ( ! empty( $password ) ) { + /** + * Fake the correct cookie to fool post_password_required(). + * Without this, get_the_content() will give a password form. + */ + require_once ABSPATH . WPINC .'/class-phpass.php'; + $hasher = new PasswordHash( 8, true ); + $value = $hasher->HashPassword( $password ); + $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = wp_slash( $value ); + } + + return $password; + } + } From a7525c57aeedad81fb5c02af1ae8154f25656014 Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 13 Sep 2016 12:21:57 +0300 Subject: [PATCH 100/318] remove password, not needed for revisions --- .../class-wp-rest-revisions-controller.php | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index 9e1df4009c..d7f54319da 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -233,20 +233,11 @@ public function prepare_item_for_response( $post, $request ) { if ( ! empty( $schema['properties']['content'] ) ) { - if ( ! empty( $post->post_password ) ) { - $this->prepare_password_response( $post->post_password ); - } - $data['content'] = array( 'raw' => $post->post_content, /** This filter is documented in wp-includes/post-template.php */ 'rendered' => apply_filters( 'the_content', $post->post_content ), ); - - // Don't leave our cookie lying around: https://github.com/WP-API/WP-API/issues/1055. - if ( ! empty( $post->post_password ) ) { - $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = ''; - } } if ( ! empty( $schema['properties']['excerpt'] ) ) { @@ -399,9 +390,6 @@ public function get_collection_params() { * @return string|null $excerpt */ protected function prepare_excerpt_response( $excerpt, $post ) { - if ( post_password_required() ) { - return __( 'There is no excerpt because this is a protected post.' ); - } /** This filter is documented in wp-includes/post-template.php */ $excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt, $post ) ); @@ -413,19 +401,4 @@ protected function prepare_excerpt_response( $excerpt, $post ) { return $excerpt; } - protected function prepare_password_response( $password ) { - if ( ! empty( $password ) ) { - /** - * Fake the correct cookie to fool post_password_required(). - * Without this, get_the_content() will give a password form. - */ - require_once ABSPATH . WPINC .'/class-phpass.php'; - $hasher = new PasswordHash( 8, true ); - $value = $hasher->HashPassword( $password ); - $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = wp_slash( $value ); - } - - return $password; - } - } From 076a54e62b84b1744db2fcd9f597b2f43bf905d7 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 14 Sep 2016 14:42:12 -0400 Subject: [PATCH 101/318] Force per_page to override the filter variable --- lib/endpoints/class-wp-rest-posts-controller.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index ca2e98e9a5..362d16f2f9 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -101,7 +101,6 @@ public function get_items( $request ) { $args['paged'] = $request['page']; $args['post__in'] = $request['include']; $args['post__not_in'] = $request['exclude']; - $args['posts_per_page'] = $request['per_page']; $args['name'] = $request['slug']; $args['post_parent__in'] = $request['parent']; $args['post_parent__not_in'] = $request['parent_exclude']; @@ -124,6 +123,9 @@ public function get_items( $request ) { unset( $args['filter'] ); } + // Ensure our per_page parameter overrides filter. + $args['posts_per_page'] = $request['per_page']; + // Force the post_type argument, since it's not a user input variable. $args['post_type'] = $this->post_type; From 0c92f76659784b6e50f256aae5e69931d8f637cc Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Wed, 14 Sep 2016 15:08:58 -0400 Subject: [PATCH 102/318] Update and add unit tests to account for ignoring filter[posts_per_page] @joehoyle @rmccue fixed the test broken in #2699 (the test presumes that filter properties work consistently, which that PR breaks by ignoring posts_per_page entirely), but I am not sure how to handle the test for `filter[posts_per_page]=-1`: should I be testing that it is ignored, as seems to be the case, or should I be testing that it is an error? The solution in #2699 is the quick fix but seems to introduce confusion around which `filter` properties are valid, and which aren't. --- tests/test-rest-posts-controller.php | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 03bd834e95..d39f5aacc0 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -420,19 +420,21 @@ public function test_get_items_pagination_headers() { $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $this->assertFalse( stripos( $headers['Link'], 'rel="next"' ) ); - // With filter params. + // With query params. $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); - $request->set_query_params( array( 'filter' => array( 'posts_per_page' => 5, 'paged' => 2 ) ) ); + $request->set_query_params( array( 'per_page' => 5, 'page' => 2 ) ); $response = $this->server->dispatch( $request ); $headers = $response->get_headers(); $this->assertEquals( 51, $headers['X-WP-Total'] ); $this->assertEquals( 11, $headers['X-WP-TotalPages'] ); $prev_link = add_query_arg( array( - 'page' => 1, + 'per_page' => 5, + 'page' => 1, ), rest_url( '/wp/v2/posts' ) ); $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); $next_link = add_query_arg( array( - 'page' => 3, + 'per_page' => 5, + 'page' => 3, ), rest_url( '/wp/v2/posts' ) ); $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); } @@ -455,6 +457,20 @@ public function test_get_items_private_filter_query_var() { $this->assertEquals( $draft_id, $data[0]['id'] ); } + public function test_get_items_invalid_per_page() { + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_query_params( array( 'per_page' => -1 ) ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + + public function test_get_items_invalid_filter_posts_per_page() { + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_query_params( array( 'filter' => array( 'posts_per_page' => -1 ) ) ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + public function test_get_items_invalid_context() { $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $request->set_param( 'context', 'banana' ); From 760e04d4952396985f5a6f19492d816dfdac5c3a Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 14 Sep 2016 16:45:48 -0400 Subject: [PATCH 103/318] Allow returning an error from field updates --- lib/endpoints/class-wp-rest-controller.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index d348f0ab42..80ecd2f115 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -330,13 +330,12 @@ protected function add_additional_fields_to_object( $object, $request ) { * * @param array $object * @param WP_REST_Request $request + * @return bool|WP_Error True on success, WP_Error object if a field cannot be updated. */ protected function update_additional_fields_for_object( $object, $request ) { - $additional_fields = $this->get_additional_fields(); foreach ( $additional_fields as $field_name => $field_options ) { - if ( ! $field_options['update_callback'] ) { continue; } @@ -346,8 +345,13 @@ protected function update_additional_fields_for_object( $object, $request ) { continue; } - call_user_func( $field_options['update_callback'], $request[ $field_name ], $object, $field_name, $request, $this->get_object_type() ); + $result = call_user_func( $field_options['update_callback'], $request[ $field_name ], $object, $field_name, $request, $this->get_object_type() ); + if ( is_wp_error( $result ) ) { + return $result; + } } + + return true; } /** From 166d0de662232cc34cbf485d640f4297c3146030 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 14 Sep 2016 16:49:09 -0400 Subject: [PATCH 104/318] Use error from field update if available --- .../class-wp-rest-attachments-controller.php | 5 ++++- lib/endpoints/class-wp-rest-comments-controller.php | 10 ++++++++-- lib/endpoints/class-wp-rest-posts-controller.php | 10 ++++++++-- lib/endpoints/class-wp-rest-terms-controller.php | 12 ++++++++++-- lib/endpoints/class-wp-rest-users-controller.php | 10 ++++++++-- 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index 7928218755..17520d4331 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -134,7 +134,10 @@ public function create_item( $request ) { update_post_meta( $id, '_wp_attachment_image_alt', sanitize_text_field( $request['alt_text'] ) ); } - $this->update_additional_fields_for_object( $attachment, $request ); + $fields_update = $this->update_additional_fields_for_object( $attachment, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $attachment, $request ); diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 3064f7b45e..37b4807b79 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -371,7 +371,10 @@ public function create_item( $request ) { } $comment = get_comment( $comment_id ); - $this->update_additional_fields_for_object( $comment, $request ); + $fields_update = $this->update_additional_fields_for_object( $comment, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } $context = current_user_can( 'moderate_comments' ) ? 'edit' : 'view'; $request->set_param( 'context', $context ); @@ -451,7 +454,10 @@ public function update_item( $request ) { } $comment = get_comment( $id ); - $this->update_additional_fields_for_object( $comment, $request ); + $fields_update = $this->update_additional_fields_for_object( $comment, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $comment, $request ); diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index ca2e98e9a5..5963d69d8a 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -340,7 +340,10 @@ public function create_item( $request ) { } $post = $this->get_post( $post_id ); - $this->update_additional_fields_for_object( $post, $request ); + $fields_update = $this->update_additional_fields_for_object( $post, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } /** * Fires after a single post is created or updated via the REST API. @@ -447,7 +450,10 @@ public function update_item( $request ) { } $post = $this->get_post( $post_id ); - $this->update_additional_fields_for_object( $post, $request ); + $fields_update = $this->update_additional_fields_for_object( $post, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } /* This action is documented in lib/endpoints/class-wp-rest-controller.php */ do_action( "rest_insert_{$this->post_type}", $post, $request, false ); diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 9dbce3494c..cdd8926d8e 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -371,7 +371,11 @@ public function create_item( $request ) { */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, true ); - $this->update_additional_fields_for_object( $term, $request ); + $fields_update = $this->update_additional_fields_for_object( $term, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } + $request->set_param( 'context', 'view' ); $response = $this->prepare_item_for_response( $term, $request ); $response = rest_ensure_response( $response ); @@ -441,7 +445,11 @@ public function update_item( $request ) { /* This action is documented in lib/endpoints/class-wp-rest-terms-controller.php */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, false ); - $this->update_additional_fields_for_object( $term, $request ); + $fields_update = $this->update_additional_fields_for_object( $term, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } + $request->set_param( 'context', 'view' ); $response = $this->prepare_item_for_response( $term, $request ); return rest_ensure_response( $response ); diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index f8dee55bc1..3abd438d1f 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -322,7 +322,10 @@ public function create_item( $request ) { array_map( array( $user, 'add_role' ), $request['roles'] ); } - $this->update_additional_fields_for_object( $user, $request ); + $fields_update = $this->update_additional_fields_for_object( $user, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } /** * Fires after a user is created or updated via the REST API. @@ -411,7 +414,10 @@ public function update_item( $request ) { array_map( array( $user, 'add_role' ), $request['roles'] ); } - $this->update_additional_fields_for_object( $user, $request ); + $fields_update = $this->update_additional_fields_for_object( $user, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } /* This action is documented in lib/endpoints/class-wp-rest-users-controller.php */ do_action( 'rest_insert_user', $user, $request, false ); From 415a41505ad8ddcd67ffbe49e320cbf6e70bae4b Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Wed, 14 Sep 2016 21:53:33 -0400 Subject: [PATCH 105/318] Fix Link The link was actually broken oops!!!! Good thing it wasn't merged. From a performance perspective the call to `WP_Comment::get_children()` is object cached from what I can tell. --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 31eae84aed..cc0fc8c9f1 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -644,7 +644,7 @@ protected function prepare_links( $comment ) { $comment_children = $comment->get_children( array( 'number' => 1 ) ); if ( ! empty( $comment_children ) ) { $args = array( 'parent' => $comment->comment_ID ); - $rest_url = add_query_arg( $args, rest_url( $this->namespace . $this->rest_base ) ); + $rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) ); $links['children'] = array( 'href' => $rest_url, From d67dd84113f08960f27f509be688479c1bd97a8b Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Thu, 15 Sep 2016 11:03:29 -0400 Subject: [PATCH 106/318] Adds boolean validation/sanitization to REST API This adds relatively robust validation/sanitization for the REST API. Boolean values can be translated from booleans, strings, and integers. --- plugin.php | 63 ++++++++++++++++++++++++++++++++-- tests/test-rest-controller.php | 51 +++++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/plugin.php b/plugin.php index 4ae237458c..d54230360d 100755 --- a/plugin.php +++ b/plugin.php @@ -299,8 +299,8 @@ function rest_validate_request_arg( $value, $request, $param ) { return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $param, 'integer' ) ); } - if ( 'boolean' === $args['type'] && ! is_bool( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $param, 'boolean' ) ); + if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) { + return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $value, 'boolean' ) ); } if ( 'string' === $args['type'] && ! is_string( $value ) ) { @@ -387,6 +387,10 @@ function rest_sanitize_request_arg( $value, $request, $param ) { return (int) $value; } + if ( 'boolean' === $args['type'] ) { + return rest_sanitize_boolean( $value ); + } + if ( isset( $args['format'] ) ) { switch ( $args['format'] ) { case 'date-time' : @@ -408,7 +412,6 @@ function rest_sanitize_request_arg( $value, $request, $param ) { return $value; } - } if ( ! function_exists( 'rest_is_ip_address' ) ) { @@ -430,3 +433,57 @@ function rest_is_ip_address( $ipv4 ) { return $ipv4; } } + +/** + * Changes a boolean-like value into the proper boolean value. + * + * @param bool|string|int $value The value being evaluated. + * @return boolean Returns the proper associated boolean value. + */ +if ( ! function_exists( 'rest_sanitize_boolean' ) ) { + function rest_sanitize_boolean( $value ) { + // String values are translated to `true`; make sure 'false' is false. + if ( is_string( $value ) ) { + $value = strtolower( $value ); + if ( in_array( $value, array( 'false', '0' ), true ) ) { + $value = false; + } + } + + // Everything else will map nicely to boolean. + return (boolean) $value; + } +} + +/** + * Determines if a given value is boolean-like. + * + * @param bool|string $maybe_bool The value being evaluated. + * @return boolean True if a boolean, otherwise false. + */ +if ( ! function_exists( 'rest_is_boolean' ) ) { + function rest_is_boolean( $maybe_bool ) { + if ( is_bool( $maybe_bool ) ) { + return true; + } + + if ( is_string( $maybe_bool ) ) { + $maybe_bool = strtolower( $maybe_bool ); + + $valid_boolean_values = array( + 'false', + 'true', + '0', + '1', + ); + + return in_array( $maybe_bool, $valid_boolean_values, true ); + } + + if ( is_int( $maybe_bool ) ) { + return in_array( $maybe_bool, array( 0, 1 ), true ); + } + + return false; + } +} diff --git a/tests/test-rest-controller.php b/tests/test-rest-controller.php index 7f0736573e..79fbca7032 100644 --- a/tests/test-rest-controller.php +++ b/tests/test-rest-controller.php @@ -52,10 +52,57 @@ public function test_validate_schema_type_boolean() { rest_validate_request_arg( false, $this->request, 'someboolean' ) ); - $this->assertErrorResponse( - 'rest_invalid_param', + $this->assertTrue( rest_validate_request_arg( 'true', $this->request, 'someboolean' ) ); + $this->assertTrue( + rest_validate_request_arg( 'TRUE', $this->request, 'someboolean' ) + ); + $this->assertTrue( + rest_validate_request_arg( 'false', $this->request, 'someboolean' ) + ); + $this->assertTrue( + rest_validate_request_arg( 'False', $this->request, 'someboolean' ) + ); + $this->assertTrue( + rest_validate_request_arg( '1', $this->request, 'someboolean' ) + ); + $this->assertTrue( + rest_validate_request_arg( '0', $this->request, 'someboolean' ) + ); + $this->assertTrue( + rest_validate_request_arg( 1, $this->request, 'someboolean' ) + ); + $this->assertTrue( + rest_validate_request_arg( 0, $this->request, 'someboolean' ) + ); + + // Check sanitize testing. + $this->assertEquals( false, + rest_sanitize_request_arg( 'false', $this->request, 'someboolean' ) + ); + $this->assertEquals( false, + rest_sanitize_request_arg( '0', $this->request, 'someboolean' ) + ); + $this->assertEquals( false, + rest_sanitize_request_arg( 0, $this->request, 'someboolean' ) + ); + $this->assertEquals( false, + rest_sanitize_request_arg( 'FALSE', $this->request, 'someboolean' ) + ); + $this->assertEquals( true, + rest_sanitize_request_arg( 'true', $this->request, 'someboolean' ) + ); + $this->assertEquals( true, + rest_sanitize_request_arg( '1', $this->request, 'someboolean' ) + ); + $this->assertEquals( true, + rest_sanitize_request_arg( 1, $this->request, 'someboolean' ) + ); + $this->assertEquals( true, + rest_sanitize_request_arg( 'TRUE', $this->request, 'someboolean' ) + ); + $this->assertErrorResponse( 'rest_invalid_param', rest_validate_request_arg( '123', $this->request, 'someboolean' ) From 68780f00d214f206c6c0b84a04d6a07056116f14 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Thu, 15 Sep 2016 13:55:18 -0400 Subject: [PATCH 107/318] "WP API" -> "WordPress REST API" in README files Fixes #2517, based on work by @davetgreen This only changes content in prose (no change to links or changelog), and varies use of "WordPress REST API", "REST API" and "API" based on context to avoid distracting repetition. --- README.md | 8 ++++---- bin/readme.txt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 85e8a3d5f8..b1c39eb8c2 100755 --- a/README.md +++ b/README.md @@ -32,11 +32,11 @@ Update user with ID 4? Send a `POST` request to `/wp-json/wp/v2/users/4`. Get al posts with the search term "awesome"? `GET /wp-json/wp/v2/posts?filter[s]=awesome`. It's that easy. -WP API exposes a simple yet easy interface to WP Query, the posts API, post -meta API, users API, revisions API and many more. Chances are, if you can do -it with WordPress, WP API will let you do it. +The WordPress REST API exposes a simple yet easy interface to WP Query, the posts +API, post meta API, users API, revisions API and many more. Chances are, if you +can do it with WordPress, the API will let you do it. -WP API also includes an easy-to-use JavaScript API based on Backbone models, +The REST API also includes an easy-to-use JavaScript API based on Backbone models, allowing plugin and theme developers to get up and running without needing to know anything about the details of getting connected. diff --git a/bin/readme.txt b/bin/readme.txt index 8eecf356be..86e18e2409 100644 --- a/bin/readme.txt +++ b/bin/readme.txt @@ -16,9 +16,9 @@ This plugin provides an easy to use REST API, available via HTTP. Grab your site Want to get your site's posts? Simply send a `GET` request to `/wp-json/wp/v2/posts`. Update user with ID 4? Send a `PUT` request to `/wp-json/wp/v2/users/4`. Get all posts with the search term "awesome"? `GET /wp-json/wp/v2/posts?filter[s]=awesome`. It's that easy. -WP API exposes a simple yet easy interface to WP Query, the posts API, post meta API, users API, revisions API and many more. Chances are, if you can do it with WordPress, WP API will let you do it. +The WordPress REST API exposes a simple yet easy interface to WP Query, the posts API, post meta API, users API, revisions API and many more. Chances are, if you can do it with WordPress, the API will let you do it. -WP API also includes an easy-to-use Javascript API based on Backbone models, allowing plugin and theme developers to get up and running without needing to know anything about the details of getting connected. +The REST API also includes an easy-to-use JavaScript API based on Backbone models, allowing plugin and theme developers to get up and running without needing to know anything about the details of getting connected. Check out [our documentation][docs] for information on what's available in the API and how to use it. We've also got documentation on extending the API with extra data for plugin and theme developers! From a6aa4ee9731c6d2ad09d6cd67b1f309c1ee7163b Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Sun, 18 Sep 2016 17:39:58 -0400 Subject: [PATCH 108/318] Add sticky parameter to the posts endpoint. This allows getting posts that are either "only sticky" for "not sticky" via `sticky=true` or `sticky=false`. This can also be used in conjuction with `include` and `exclude`. --- .../class-wp-rest-posts-controller.php | 30 ++++++ tests/test-rest-posts-controller.php | 92 +++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 469d763f0c..6ea5201b6a 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -118,6 +118,28 @@ public function get_items( $request ) { unset( $args['filter'] ); } + if ( isset( $request['sticky'] ) ) { + $sticky_posts = get_option( 'sticky_posts', array() ); + if ( $sticky_posts && $request['sticky'] ) { + // as post__in will be used to only get sticky posts, + // we have to support the case where post__in was already + // specified. + $args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts; + + // if we intersected, but there are no post ids in common, + // WP_Query won't return "no posts" for `post__in = array()` + // so we have to fake it a bit. + if ( ! $args['post__in'] ) { + $args['post__in'] = array( -1 ); + } + } elseif ( $sticky_posts ) { + // as post___not_in will be used to only get posts that + // are not sticky, we have to support the case where post__not_in + // was already specified. + $args['post__not_in'] = array_merge( $args['post__not_in'], $sticky_posts ); + } + } + // Force the post_type argument, since it's not a user input variable. $args['post_type'] = $this->post_type; @@ -1723,6 +1745,14 @@ public function get_collection_params() { 'default' => array(), ); } + + if ( 'post' === $this->post_type ) { + $params['sticky'] = array( + 'description' => __( 'Limit result set to items that are sticky.' ), + 'type' => 'boolean', + ); + } + return $params; } diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index f5bf647c1f..159e97b1ba 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -74,6 +74,7 @@ public function test_registered_query_params() { 'search', 'slug', 'status', + 'sticky', 'tags', ), $keys ); } @@ -347,6 +348,84 @@ public function test_get_items_tags_and_categories_query() { $this->assertCount( 1, $response->get_data() ); } + public function test_get_items_sticky_query() { + $id1 = $this->post_id; + $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + + update_option( 'sticky_posts', array( $id2 ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'sticky', true ); + + $response = $this->server->dispatch( $request ); + $this->assertCount( 1, $response->get_data() ); + + $post = $response->get_data()[0]; + $this->assertEquals( $id2, $post['id'] ); + } + + public function test_get_items_sticky_with_post__in_query() { + $id1 = $this->post_id; + $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id3 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + + update_option( 'sticky_posts', array( $id2 ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'sticky', true ); + $request->set_param( 'include', array( $id1 ) ); + + $response = $this->server->dispatch( $request ); + $this->assertCount( 0, $response->get_data() ); + + update_option( 'sticky_posts', array( $id1, $id2 ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'sticky', true ); + $request->set_param( 'include', array( $id1 ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertCount( 1, $response->get_data() ); + + $post = $response->get_data()[0]; + $this->assertEquals( $id1, $post['id'] ); + } + + public function test_get_items_not_sticky_query() { + $id1 = $this->post_id; + $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + + update_option( 'sticky_posts', array( $id2 ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'sticky', false ); + + $response = $this->server->dispatch( $request ); + $this->assertCount( 1, $response->get_data() ); + + $post = $response->get_data()[0]; + $this->assertEquals( $id1, $post['id'] ); + } + + public function test_get_items_sticky_with_post__not_in_query() { + $id1 = $this->post_id; + $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id3 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + + update_option( 'sticky_posts', array( $id2 ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'sticky', false ); + $request->set_param( 'exclude', array( $id3 ) ); + + $response = $this->server->dispatch( $request ); + $this->assertCount( 1, $response->get_data() ); + + $post = $response->get_data()[0]; + $this->assertEquals( $id1, $post['id'] ); + } + /** * @group test */ @@ -1431,6 +1510,19 @@ public function test_update_post_empty_content() { $this->assertEquals( '', $new_data['content']['raw'] ); } + public function test_update_post_with_slashes_in_content() { + wp_set_current_user( $this->editor_id ); + + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( array( + 'content' => 'Hello\\s', + ) ); + + $response = $this->server->dispatch( $request ); + $new_data = $response->get_data(); + $this->assertEquals( 'Hello\\s', $new_data['content']['raw'] ); + } + public function test_update_post_with_password_and_sticky_fails() { wp_set_current_user( $this->editor_id ); From d71012db18d3953cb11a824056b1a226118c8867 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Mon, 19 Sep 2016 10:16:16 -0400 Subject: [PATCH 109/318] Update test to reflect team decision to ignore posts_per_page --- tests/test-rest-posts-controller.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index d39f5aacc0..9f21da5817 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -464,11 +464,16 @@ public function test_get_items_invalid_per_page() { $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } - public function test_get_items_invalid_filter_posts_per_page() { + public function test_get_items_invalid_posts_per_page_ignored() { + // This test ensures that filter[posts_per_page] is ignored, and that -1 + // cannot be used to sidestep per_page's valid range to retrieve all posts + for ( $i = 0; $i < 20; $i++ ) { + $this->factory->post->create( array( 'post_status' => 'publish' ) ); + } $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $request->set_query_params( array( 'filter' => array( 'posts_per_page' => -1 ) ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + $this->assertCount( 10, $response->get_data() ); } public function test_get_items_invalid_context() { From c6f6aef636fa546c05493d5bd68036908b6fb841 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Thu, 22 Sep 2016 11:06:31 -0400 Subject: [PATCH 110/318] Fix typo (PUT vs POST) in readme.md; readme.txt is correct --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1c39eb8c2..d35818e633 100755 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ site's data in simple JSON format, including users, posts, taxonomies and more. Retrieving or updating data is as simple as sending a HTTP request. Want to get your site's posts? Simply send a `GET` request to `/wp-json/wp/v2/posts`. -Update user with ID 4? Send a `POST` request to `/wp-json/wp/v2/users/4`. Get all +Update user with ID 4? Send a `PUT` request to `/wp-json/wp/v2/users/4`. Get all posts with the search term "awesome"? `GET /wp-json/wp/v2/posts?filter[s]=awesome`. It's that easy. From f5bb32c06ef25dc72befaf3c5fd13e412ca0011e Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 22 Sep 2016 11:50:21 -0400 Subject: [PATCH 111/318] Add Codecov configuration --- .codecov.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .codecov.yml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000000..69cb76019a --- /dev/null +++ b/.codecov.yml @@ -0,0 +1 @@ +comment: false From 760d19a804642936d9520c43f3d4dfbb86f7822a Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Thu, 22 Sep 2016 13:14:43 -0400 Subject: [PATCH 112/318] Add in count parameter. Adding in the count query parameter. As rmccue pointed out, it is better to query for the count as opposed to chaching the actual fields into the object cache, because all we really need is the count. --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 5e7af0e826..b679e6b2f1 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -647,7 +647,7 @@ protected function prepare_links( $comment ) { } // Only grab one comment to verify the comment has children. - $comment_children = $comment->get_children( array( 'number' => 1 ) ); + $comment_children = $comment->get_children( array( 'number' => 1, 'count' => true ) ); if ( ! empty( $comment_children ) ) { $args = array( 'parent' => $comment->comment_ID ); $rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) ); From f76e907d217dfaec945dc1551c2c6a301eebc5d9 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 14:47:35 -0400 Subject: [PATCH 113/318] Remove test that was accidentally committed --- tests/test-rest-posts-controller.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index c4df19db1c..39b48b5654 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -1520,19 +1520,6 @@ public function test_update_post_empty_content() { $this->assertEquals( '', $new_data['content']['raw'] ); } - public function test_update_post_with_slashes_in_content() { - wp_set_current_user( $this->editor_id ); - - $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( array( - 'content' => 'Hello\\s', - ) ); - - $response = $this->server->dispatch( $request ); - $new_data = $response->get_data(); - $this->assertEquals( 'Hello\\s', $new_data['content']['raw'] ); - } - public function test_update_post_with_password_and_sticky_fails() { wp_set_current_user( $this->editor_id ); From b6bf810f80c9779460fee2cce6f2c57f0337989f Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 15:05:07 -0400 Subject: [PATCH 114/318] Fix syntax error in 5.2 --- tests/test-rest-posts-controller.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 39b48b5654..73c6929baf 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -370,7 +370,8 @@ public function test_get_items_sticky_query() { $response = $this->server->dispatch( $request ); $this->assertCount( 1, $response->get_data() ); - $post = $response->get_data()[0]; + $posts = $response->get_data(); + $post = $posts[0]; $this->assertEquals( $id2, $post['id'] ); } @@ -398,7 +399,8 @@ public function test_get_items_sticky_with_post__in_query() { $this->assertCount( 1, $response->get_data() ); - $post = $response->get_data()[0]; + $posts = $response->get_data(); + $post = $posts[0]; $this->assertEquals( $id1, $post['id'] ); } @@ -414,7 +416,8 @@ public function test_get_items_not_sticky_query() { $response = $this->server->dispatch( $request ); $this->assertCount( 1, $response->get_data() ); - $post = $response->get_data()[0]; + $posts = $response->get_data(); + $post = $posts[0]; $this->assertEquals( $id1, $post['id'] ); } @@ -432,7 +435,8 @@ public function test_get_items_sticky_with_post__not_in_query() { $response = $this->server->dispatch( $request ); $this->assertCount( 1, $response->get_data() ); - $post = $response->get_data()[0]; + $posts = $response->get_data(); + $post = $posts[0]; $this->assertEquals( $id1, $post['id'] ); } From ce8bde7a56f2cd4d6307fbb52a061575300ec531 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 15:17:58 -0400 Subject: [PATCH 115/318] Small stylistic fix --- lib/endpoints/class-wp-rest-posts-controller.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 87368dc061..5c525ee20e 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -127,19 +127,19 @@ public function get_items( $request ) { if ( isset( $request['sticky'] ) ) { $sticky_posts = get_option( 'sticky_posts', array() ); if ( $sticky_posts && $request['sticky'] ) { - // as post__in will be used to only get sticky posts, + // As post__in will be used to only get sticky posts, // we have to support the case where post__in was already // specified. $args['post__in'] = $args['post__in'] ? array_intersect( $sticky_posts, $args['post__in'] ) : $sticky_posts; - // if we intersected, but there are no post ids in common, + // If we intersected, but there are no post ids in common, // WP_Query won't return "no posts" for `post__in = array()` // so we have to fake it a bit. if ( ! $args['post__in'] ) { $args['post__in'] = array( -1 ); } } elseif ( $sticky_posts ) { - // as post___not_in will be used to only get posts that + // As post___not_in will be used to only get posts that // are not sticky, we have to support the case where post__not_in // was already specified. $args['post__not_in'] = array_merge( $args['post__not_in'], $sticky_posts ); @@ -1801,8 +1801,8 @@ public function get_collection_params() { if ( 'post' === $this->post_type ) { $params['sticky'] = array( - 'description' => __( 'Limit result set to items that are sticky.' ), - 'type' => 'boolean', + 'description' => __( 'Limit result set to items that are sticky.' ), + 'type' => 'boolean', ); } From b10fb736dff1ecb7addad4c91f24fccb6986c8a6 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Thu, 22 Sep 2016 15:55:11 -0400 Subject: [PATCH 116/318] Remove Embeddability Generally Embeddable links should not be collections or big amounts of data. --- lib/endpoints/class-wp-rest-comments-controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index b679e6b2f1..8f83ae2365 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -654,7 +654,6 @@ protected function prepare_links( $comment ) { $links['children'] = array( 'href' => $rest_url, - 'embeddable' => true, ); } From 3541909576beac60c1725448f56708421f9bb1aa Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 16:47:59 -0400 Subject: [PATCH 117/318] Allow reading of password protected posts, and set the content to an empty string if the user does not have access. --- .../class-wp-rest-posts-controller.php | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 5963d69d8a..facfcade65 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -37,7 +37,10 @@ public function register_routes() { 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ), 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + 'password' => array( + 'description' => __( 'The password for the post if it is password protected.' ), + ), ), ), array( @@ -75,6 +78,11 @@ public function get_items_permissions_check( $request ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); } + // Allow access to all password protected posts if the context is edit. + if ( 'edit' === $request['context'] ) { + add_filter( 'post_password_required', '__return_false' ); + } + return true; } @@ -224,6 +232,20 @@ public function get_item_permissions_check( $request ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post' ), array( 'status' => rest_authorization_required_code() ) ); } + if ( $post && ! empty( $request['password'] ) ) { + if ( ! hash_equals( $post->post_password, $request['password'] ) ) { + return new WP_Error( 'rest_post_incorrect_password', __( 'Incorrect post password.' ), array( 'status' => 403 ) ); + } else { + // Allow access to the post going forward. + add_filter( 'post_password_required', '__return_false' ); + } + } + + // Allow access to all password protected posts if the context is edit. + if ( 'edit' === $request['context'] ) { + add_filter( 'post_password_required', '__return_false' ); + } + if ( $post ) { return $this->check_read_permission( $post ); } @@ -990,10 +1012,6 @@ protected function check_is_post_type_allowed( $post_type ) { * @return boolean Can we read it? */ public function check_read_permission( $post ) { - if ( ! empty( $post->post_password ) && ! $this->check_update_permission( $post ) ) { - return false; - } - $post_type = get_post_type_object( $post->post_type ); if ( ! $this->check_is_post_type_allowed( $post_type ) ) { return false; @@ -1137,10 +1155,12 @@ public function prepare_item_for_response( $post, $request ) { } if ( ! empty( $schema['properties']['title'] ) ) { + add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); $data['title'] = array( 'raw' => $post->post_title, 'rendered' => get_the_title( $post->ID ), ); + remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); } if ( ! empty( $schema['properties']['content'] ) ) { @@ -1150,15 +1170,11 @@ public function prepare_item_for_response( $post, $request ) { } $data['content'] = array( - 'raw' => $post->post_content, + 'raw' => $post->post_content, /** This filter is documented in wp-includes/post-template.php */ - 'rendered' => apply_filters( 'the_content', $post->post_content ), + 'rendered' => post_password_required( $post ) ? '' : apply_filters( 'the_content', $post->post_content ), + 'protected' => (bool) $post->post_password, ); - - // Don't leave our cookie lying around: https://github.com/WP-API/WP-API/issues/1055. - if ( ! empty( $post->post_password ) ) { - $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = ''; - } } if ( ! empty( $schema['properties']['excerpt'] ) ) { @@ -1414,11 +1430,6 @@ public function get_item_schema() { 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'password' => array( - 'description' => __( 'A password to protect access to the post.' ), - 'type' => 'string', - 'context' => array( 'edit' ), - ), 'slug' => array( 'description' => __( 'An alphanumeric identifier for the object unique to its type.' ), 'type' => 'string', @@ -1537,6 +1548,12 @@ public function get_item_schema() { 'context' => array( 'view', 'edit' ), 'readonly' => true, ), + 'protected' => array( + 'description' => __( 'Whether the content is protected with a password.' ), + 'type' => 'boolean', + 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, + ), ), ); break; @@ -1566,6 +1583,12 @@ public function get_item_schema() { 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, ), + 'protected' => array( + 'description' => __( 'Whether the excerpt is protected with a password.' ), + 'type' => 'boolean', + 'context' => array( 'view', 'edit', 'embed' ), + 'readonly' => true, + ), ), ); break; @@ -1619,6 +1642,12 @@ public function get_item_schema() { 'type' => 'boolean', 'context' => array( 'view', 'edit' ), ); + + $schema['properties']['password'] = array( + 'description' => __( 'A password to protect access to the content and excerpt.' ), + 'type' => 'string', + 'context' => array( 'edit' ), + ); } if ( 'page' === $this->post_type ) { From fb045337f6d20528337e515c2938c6bc70d79254 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 16:49:10 -0400 Subject: [PATCH 118/318] Don't show "Protected: " on protected post titles in the rest api. --- .../class-wp-rest-posts-controller.php | 70 +++++-------------- 1 file changed, 19 insertions(+), 51 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index facfcade65..a6479d72d5 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -287,10 +287,6 @@ public function create_item_permissions_check( $request ) { $post_type = get_post_type_object( $this->post_type ); - if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) { - return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); - } - if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) { return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to create posts as this user.' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -400,10 +396,6 @@ public function update_item_permissions_check( $request ) { return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to update this post.' ), array( 'status' => rest_authorization_required_code() ) );; } - if ( ! empty( $request['password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) { - return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create password protected posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); - } - if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) { return new WP_Error( 'rest_cannot_edit_others', __( 'You are not allowed to update posts as this user.' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -682,27 +674,6 @@ protected function get_allowed_query_vars() { return $valid_vars; } - /** - * Check the post excerpt and prepare it for single post output. - * - * @param string $excerpt - * @return string|null $excerpt - */ - protected function prepare_excerpt_response( $excerpt, $post ) { - if ( post_password_required() ) { - return __( 'There is no excerpt because this is a protected post.' ); - } - - /** This filter is documented in wp-includes/post-template.php */ - $excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt, $post ) ); - - if ( empty( $excerpt ) ) { - return ''; - } - - return $excerpt; - } - /** * Check the post_date_gmt or modified_gmt and prepare any post or * modified date for single post output. @@ -726,21 +697,6 @@ protected function prepare_date_response( $date_gmt, $date = null ) { return mysql_to_rfc3339( $date_gmt ); } - protected function prepare_password_response( $password ) { - if ( ! empty( $password ) ) { - /** - * Fake the correct cookie to fool post_password_required(). - * Without this, get_the_content() will give a password form. - */ - require_once ABSPATH . WPINC .'/class-phpass.php'; - $hasher = new PasswordHash( 8, true ); - $value = $hasher->HashPassword( $password ); - $_COOKIE[ 'wp-postpass_' . COOKIEHASH ] = wp_slash( $value ); - } - - return $password; - } - /** * Prepare a single post for create or update. * @@ -1164,11 +1120,6 @@ public function prepare_item_for_response( $post, $request ) { } if ( ! empty( $schema['properties']['content'] ) ) { - - if ( ! empty( $post->post_password ) ) { - $this->prepare_password_response( $post->post_password ); - } - $data['content'] = array( 'raw' => $post->post_content, /** This filter is documented in wp-includes/post-template.php */ @@ -1178,9 +1129,12 @@ public function prepare_item_for_response( $post, $request ) { } if ( ! empty( $schema['properties']['excerpt'] ) ) { + /** This filter is documented in wp-includes/post-template.php */ + $excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $post->post_excerpt, $post ) ); $data['excerpt'] = array( - 'raw' => $post->post_excerpt, - 'rendered' => $this->prepare_excerpt_response( $post->post_excerpt, $post ), + 'raw' => $post->post_excerpt, + 'rendered' => post_password_required( $post ) ? '' : $excerpt, + 'protected' => (bool) $post->post_password, ); } @@ -1259,6 +1213,20 @@ public function prepare_item_for_response( $post, $request ) { return apply_filters( "rest_prepare_{$this->post_type}", $response, $post, $request ); } + /** + * Overwrite the default protected title format. + * + * By default WordPress will show password protected posts with a title of + * "Protected: %s", as the REST API communicates the protected status of a post + * in a machine readable format, we remove the "Protected: " prefix. + * + * @param string $format + * @return string + */ + public function protected_title_format() { + return '%s'; + } + /** * Prepare links for the request. * From 522e146d874c393c7bf2d3bf5d8d64cc818df55a Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 16:49:39 -0400 Subject: [PATCH 119/318] Only test the existance for the password field for post_type===post --- tests/class-wp-test-rest-post-type-controller-testcase.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/class-wp-test-rest-post-type-controller-testcase.php b/tests/class-wp-test-rest-post-type-controller-testcase.php index fb4324a1e0..81f07ced6b 100644 --- a/tests/class-wp-test-rest-post-type-controller-testcase.php +++ b/tests/class-wp-test-rest-post-type-controller-testcase.php @@ -63,6 +63,9 @@ protected function check_post_data( $post, $data, $context, $links ) { $this->assertEquals( is_sticky( $post->ID ), $data['sticky'] ); } + if ( 'post' === $post->post_type && 'edit' === $context ) { + $this->assertEquals( $post->post_password, $data['password'] ); + } if ( 'page' === $post->post_type ) { $this->assertEquals( get_page_template_slug( $post->ID ), $data['template'] ); } @@ -130,7 +133,6 @@ protected function check_post_data( $post, $data, $context, $links ) { if ( 'edit' === $context ) { $this->assertEquals( $post->guid, $data['guid']['raw'] ); $this->assertEquals( $post->post_status, $data['status'] ); - $this->assertEquals( $post->post_password, $data['password'] ); if ( '0000-00-00 00:00:00' === $post->post_date_gmt ) { $this->assertNull( $data['date_gmt'] ); From b4031923aabc4a8c0a8ee1b6b3dfc364604431af Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 16:50:01 -0400 Subject: [PATCH 120/318] Account for the fact the titles wont' have "Protected: " in them anymore --- ...wp-test-rest-post-type-controller-testcase.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/class-wp-test-rest-post-type-controller-testcase.php b/tests/class-wp-test-rest-post-type-controller-testcase.php index 81f07ced6b..75ba724207 100644 --- a/tests/class-wp-test-rest-post-type-controller-testcase.php +++ b/tests/class-wp-test-rest-post-type-controller-testcase.php @@ -90,7 +90,9 @@ protected function check_post_data( $post, $data, $context, $links ) { // Check filtered values. if ( post_type_supports( $post->post_type, 'title' ) ) { + add_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); $this->assertEquals( get_the_title( $post->ID ), $data['title']['rendered'] ); + remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); if ( 'edit' === $context ) { $this->assertEquals( $post->post_title, $data['title']['raw'] ); } else { @@ -291,4 +293,17 @@ protected function set_raw_post_data( $args = array() ) { ) ) ); } + /** + * Overwrite the default protected title format. + * + * By default WordPress will show password protected posts with a title of + * "Protected: %s", as the REST API communicates the protected status of a post + * in a machine readable format, we remove the "Protected: " prefix. + * + * @param string $format + * @return string + */ + public function protected_title_format() { + return '%s'; + } } From 4dcd4eb38fcd209f4dadb51ca7b367800860a7fc Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 16:50:30 -0400 Subject: [PATCH 121/318] Only test excerts and content for non-password protected posts. This is pretty dificult to do anutomatically, as we don't have access to the request vars so we don't know if the posts should have a valid content or not. --- .../class-wp-test-rest-post-type-controller-testcase.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/class-wp-test-rest-post-type-controller-testcase.php b/tests/class-wp-test-rest-post-type-controller-testcase.php index 75ba724207..aa41af235a 100644 --- a/tests/class-wp-test-rest-post-type-controller-testcase.php +++ b/tests/class-wp-test-rest-post-type-controller-testcase.php @@ -66,6 +66,7 @@ protected function check_post_data( $post, $data, $context, $links ) { if ( 'post' === $post->post_type && 'edit' === $context ) { $this->assertEquals( $post->post_password, $data['password'] ); } + if ( 'page' === $post->post_type ) { $this->assertEquals( get_page_template_slug( $post->ID ), $data['template'] ); } @@ -104,7 +105,10 @@ protected function check_post_data( $post, $data, $context, $links ) { if ( post_type_supports( $post->post_type, 'editor' ) ) { // TODO: apply content filter for more accurate testing. - $this->assertEquals( wpautop( $post->post_content ), $data['content']['rendered'] ); + if ( ! $post->post_password ) { + $this->assertEquals( wpautop( $post->post_content ), $data['content']['rendered'] ); + } + if ( 'edit' === $context ) { $this->assertEquals( $post->post_content, $data['content']['raw'] ); } else { @@ -119,7 +123,7 @@ protected function check_post_data( $post, $data, $context, $links ) { // TODO: apply excerpt filter for more accurate testing. $this->assertEquals( wpautop( $post->post_excerpt ), $data['excerpt']['rendered'] ); } else { - $this->assertEquals( 'There is no excerpt because this is a protected post.', $data['excerpt']['rendered'] ); + // TODO: better testing for excerpts for password protected posts. } if ( 'edit' === $context ) { $this->assertEquals( $post->post_excerpt, $data['excerpt']['raw'] ); From c0feec2852704fd9f2ea42a3dae51bab72d3a3e8 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 16:51:27 -0400 Subject: [PATCH 122/318] Update unit tests for new password protected posts handling. --- tests/test-rest-posts-controller.php | 63 ++++++++++++++++++---------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 03bd834e95..aa68f45871 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -605,22 +605,61 @@ public function test_get_post_with_password() { 'post_password' => '$inthebananastand', ) ); - wp_set_current_user( $this->editor_id ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $response = $this->server->dispatch( $request ); + + $this->check_get_post_response( $response, 'view' ); + + $data = $response->get_data(); + $this->assertTrue( $data['content']['protected'] ); + $this->assertTrue( $data['excerpt']['protected'] ); + } + + public function test_get_post_with_password_using_password() { + $post_id = $this->factory->post->create( array( + 'post_password' => '$inthebananastand', + 'post_content' => 'Some secret content.', + 'post_excerpt' => 'Some secret excerpt.', + ) ); + $post = get_post( $post_id ); $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_param( 'password', '$inthebananastand' ); $response = $this->server->dispatch( $request ); $this->check_get_post_response( $response, 'view' ); + + $data = $response->get_data(); + $this->assertEquals( wpautop( $post->post_content ), $data['content']['rendered'] ); + $this->assertEquals( wpautop( $post->post_excerpt ), $data['excerpt']['rendered'] ); + } + + public function test_get_post_with_password_using_incorrect_password() { + $post_id = $this->factory->post->create( array( + 'post_password' => '$inthebananastand', + ) ); + + $post = get_post( $post_id ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $post_id ) ); + $request->set_param( 'password', 'wrongpassword' ); + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_post_incorrect_password', $response, 403 ); } public function test_get_post_with_password_without_permission() { $post_id = $this->factory->post->create( array( 'post_password' => '$inthebananastand', + 'post_content' => 'Some secret content.', + 'post_excerpt' => 'Some secret excerpt.', ) ); $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $post_id ) ); $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->check_get_post_response( $response, 'view' ); + $this->assertEquals( '', $data['content']['rendered'] ); + $this->assertEquals( '', $data['excerpt']['rendered'] ); - $this->assertErrorResponse( 'rest_forbidden', $response, 403 ); } public function test_get_item_read_permission_custom_post_status() { @@ -940,26 +979,6 @@ public function test_create_post_with_password() { $this->assertEquals( 'testing', $data['password'] ); } - public function test_create_post_with_password_without_permission() { - wp_set_current_user( $this->author_id ); - $user = wp_get_current_user(); - $user->add_cap( 'publish_posts', false ); - // Flush capabilities, https://core.trac.wordpress.org/ticket/28374 - $user->get_role_caps(); - $user->update_user_level_from_caps(); - - $request = new WP_REST_Request( 'POST', '/wp/v2/posts' ); - $params = $this->set_post_data( array( - 'password' => 'testing', - 'author' => $this->author_id, - 'status' => 'draft', - ) ); - $request->set_body_params( $params ); - $response = $this->server->dispatch( $request ); - - $this->assertErrorResponse( 'rest_cannot_publish', $response, 403 ); - } - public function test_create_post_with_falsy_password() { wp_set_current_user( $this->editor_id ); From 939849d612a7e5531efbd5c8fccf9b1ff2f0f3df Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 20:06:56 -0400 Subject: [PATCH 123/318] Ability to orderby slug, email and url on users endpoints. --- lib/endpoints/class-wp-rest-users-controller.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 3abd438d1f..324ff10807 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -86,6 +86,10 @@ public function get_items_permissions_check( $request ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view this resource with edit context.' ), array( 'status' => rest_authorization_required_code() ) ); } + if ( in_array( $request['orderby'], array( 'email' ), true ) && ! current_user_can( 'list_users' ) ) { + return new WP_Error( 'rest_forbidden_orderby', __( 'Sorry, you cannot order by this parameter.' ), array( 'status' => rest_authorization_required_code() ) ); + } + return true; } @@ -112,6 +116,9 @@ public function get_items( $request ) { 'include' => 'include', 'name' => 'display_name', 'registered_date' => 'registered', + 'slug' => 'user_nicename', + 'email' => 'user_email', + 'url' => 'user_url', ); $prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ]; $prepared_args['search'] = $request['search']; @@ -908,6 +915,9 @@ public function get_collection_params() { 'include', 'name', 'registered_date', + 'slug', + 'email', + 'url', ), 'sanitize_callback' => 'sanitize_key', 'type' => 'string', From efe2035f02ab9bf426d9e4b87f48953ec4513c3a Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 20:07:12 -0400 Subject: [PATCH 124/318] Unit tests for new orderbys --- tests/test-rest-users-controller.php | 99 +++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index c48176f34e..b9f32b0d84 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -238,7 +238,7 @@ public function test_get_items_page() { $this->assertContains( '<' . $prev_link . '>; rel="prev"', $headers['Link'] ); } - public function test_get_items_orderby() { + public function test_get_items_orderby_name() { wp_set_current_user( $this->user ); $low_id = $this->factory->user->create( array( 'display_name' => 'AAAAA' ) ); $mid_id = $this->factory->user->create( array( 'display_name' => 'NNNNN' ) ); @@ -259,6 +259,103 @@ public function test_get_items_orderby() { $this->assertEquals( $low_id, $data[0]['id'] ); } + public function test_get_items_orderby_url() { + wp_set_current_user( $this->user ); + + $low_id = $this->factory->user->create( array( 'user_url' => 'http://a.com' ) ); + $high_id = $this->factory->user->create( array( 'user_url' => 'http://b.com' ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'orderby', 'url' ); + $request->set_param( 'order', 'desc' ); + $request->set_param( 'per_page', 1 ); + $request->set_param( 'include', array( $low_id, $high_id ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( $high_id, $data[0]['id'] ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'orderby', 'url' ); + $request->set_param( 'order', 'asc' ); + $request->set_param( 'per_page', 1 ); + $request->set_param( 'include', array( $low_id, $high_id ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( $low_id, $data[0]['id'] ); + } + + public function test_get_items_orderby_slug() { + wp_set_current_user( $this->user ); + + $high_id = $this->factory->user->create( array( 'user_nicename' => 'blogin' ) ); + $low_id = $this->factory->user->create( array( 'user_nicename' => 'alogin' ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'orderby', 'slug' ); + $request->set_param( 'order', 'desc' ); + $request->set_param( 'per_page', 1 ); + $request->set_param( 'include', array( $low_id, $high_id ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( $high_id, $data[0]['id'] ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'orderby', 'slug' ); + $request->set_param( 'order', 'asc' ); + $request->set_param( 'per_page', 1 ); + $request->set_param( 'include', array( $low_id, $high_id ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( $low_id, $data[0]['id'] ); + } + + public function test_get_items_orderby_email() { + wp_set_current_user( $this->user ); + + $high_id = $this->factory->user->create( array( 'user_email' => 'bemail@gmail.com' ) ); + $low_id = $this->factory->user->create( array( 'user_email' => 'aemail@gmail.com' ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'orderby', 'email' ); + $request->set_param( 'order', 'desc' ); + $request->set_param( 'per_page', 1 ); + $request->set_param( 'include', array( $low_id, $high_id ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( $high_id, $data[0]['id'] ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'orderby', 'email' ); + $request->set_param( 'order', 'asc' ); + $request->set_param( 'per_page', 1 ); + $request->set_param( 'include', array( $low_id, $high_id ) ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( $low_id, $data[0]['id'] ); + } + + public function test_get_items_orderby_email_unauthenticaed() { + $high_id = $this->factory->user->create( array( 'user_email' => 'bemail@gmail.com' ) ); + $low_id = $this->factory->user->create( array( 'user_email' => 'aemail@gmail.com' ) ); + + $this->factory->post->create( array( + 'post_author' => $high_id, + )); + $this->factory->post->create( array( + 'post_author' => $low_id, + )); + + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'orderby', 'email' ); + $request->set_param( 'order', 'desc' ); + $request->set_param( 'per_page', 1 ); + $request->set_param( 'include', array( $low_id, $high_id ) ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_forbidden_orderby', $response, 401 ); + } + public function test_get_items_offset() { wp_set_current_user( $this->user ); // 2 users created in __construct(), plus default user From dc3f4f9ac57a4ae38e293e6ef87ce20817354058 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 20:11:36 -0400 Subject: [PATCH 125/318] Only add asterisks to the user query if there is a query present --- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 3abd438d1f..4b9e2923b9 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -121,7 +121,7 @@ public function get_items( $request ) { $prepared_args['has_published_posts'] = true; } - if ( '' !== $prepared_args['search'] ) { + if ( ! empty( $prepared_args['search'] ) ) { $prepared_args['search'] = '*' . $prepared_args['search'] . '*'; } From 0fd57e78cee86cb779e965f9a9abc93b3b94085e Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 22 Sep 2016 21:05:48 -0400 Subject: [PATCH 126/318] Use wrapper for `sanitize_title` to avoid messed up slugs. This is required sinse passing sanitize_title directly will result in messed up slugs due to the `context` param getting a garbage value. --- lib/endpoints/class-wp-rest-posts-controller.php | 14 +++++++++++++- tests/test-rest-posts-controller.php | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 5963d69d8a..c291ecd05c 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1424,7 +1424,7 @@ public function get_item_schema() { 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( - 'sanitize_callback' => 'sanitize_title', + 'sanitize_callback' => array( $this, 'sanitize_slug' ), ), ), 'status' => array( @@ -1798,4 +1798,16 @@ public function validate_user_can_query_private_statuses( $value, $request, $par return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden' ), array( 'status' => rest_authorization_required_code() ) ); } + /** + * Sanitize a post's title. + * + * It's not possible to pass `sanitize_title` directly as the sanitize_callback + * as that will cause 3 params inadvertantly being passed to `sanitize_title`. + * + * @param string $slug + * @return string + */ + public function sanitize_slug( $slug ) { + return sanitize_title( $slug ); + } } diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 03bd834e95..1a89479864 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -1360,6 +1360,22 @@ public function test_update_post_slug() { $this->assertEquals( 'sample-slug', $post->post_name ); } + public function test_update_post_slug_accented_chars() { + wp_set_current_user( $this->editor_id ); + + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $params = $this->set_post_data( array( + 'slug' => 'tęst-acceńted-chäræcters', + ) ); + $request->set_body_params( $params ); + $response = $this->server->dispatch( $request ); + + $new_data = $response->get_data(); + $this->assertEquals( 'test-accented-charaecters', $new_data['slug'] ); + $post = get_post( $new_data['id'] ); + $this->assertEquals( 'test-accented-charaecters', $post->post_name ); + } + public function test_update_post_sticky() { wp_set_current_user( $this->editor_id ); From 27791708ec00c7a30f9cd34eadd66c2f0cad7959 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 22 Sep 2016 21:09:29 -0400 Subject: [PATCH 127/318] Ensure the terms list is a list A filter could unset() items from this list, which would cause it to be accidentally output as an object. --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 5963d69d8a..95b81f00cf 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1217,7 +1217,7 @@ public function prepare_item_for_response( $post, $request ) { $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; if ( ! empty( $schema['properties'][ $base ] ) ) { $terms = get_the_terms( $post, $taxonomy->name ); - $data[ $base ] = $terms ? wp_list_pluck( $terms, 'term_id' ) : array(); + $data[ $base ] = $terms ? array_values( wp_list_pluck( $terms, 'term_id' ) ) : array(); } } From 24e290bd551f84247a8a4d24abb99e5b241d72f7 Mon Sep 17 00:00:00 2001 From: Vishal Kakadiya Date: Fri, 23 Sep 2016 10:24:01 +0530 Subject: [PATCH 128/318] Added @return on handle_featured_media() --- lib/endpoints/class-wp-rest-posts-controller.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 95b81f00cf..391ed7a477 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -909,6 +909,8 @@ protected function handle_status_param( $post_status, $post_type ) { * * @param int $featured_media * @param int $post_id + * + * @return bool|WP_Error */ protected function handle_featured_media( $featured_media, $post_id ) { From d1eed8e2934be609733a388673af92a066fa4e6a Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Fri, 23 Sep 2016 12:39:59 -0400 Subject: [PATCH 129/318] Move sanitize_slug to parent controller --- lib/endpoints/class-wp-rest-controller.php | 13 +++++++++++++ lib/endpoints/class-wp-rest-posts-controller.php | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 80ecd2f115..211a8f59b3 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -514,4 +514,17 @@ public function get_post( $post ) { return $post; } + + /** + * Sanitize a post's title. + * + * It's not possible to pass `sanitize_title` directly as the sanitize_callback + * as that will cause 3 params inadvertantly being passed to `sanitize_title`. + * + * @param string $slug + * @return string + */ + public function sanitize_slug( $slug ) { + return sanitize_title( $slug ); + } } diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index c291ecd05c..c43bc16d20 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1797,17 +1797,4 @@ public function validate_user_can_query_private_statuses( $value, $request, $par } return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden' ), array( 'status' => rest_authorization_required_code() ) ); } - - /** - * Sanitize a post's title. - * - * It's not possible to pass `sanitize_title` directly as the sanitize_callback - * as that will cause 3 params inadvertantly being passed to `sanitize_title`. - * - * @param string $slug - * @return string - */ - public function sanitize_slug( $slug ) { - return sanitize_title( $slug ); - } } From 4becabc1038503d5ba72fe61e8e2080f1bea726d Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Fri, 23 Sep 2016 12:41:13 -0400 Subject: [PATCH 130/318] Use sanitize_slug in other controllers --- lib/endpoints/class-wp-rest-terms-controller.php | 2 +- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index cdd8926d8e..0c3fcfede2 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -719,7 +719,7 @@ public function get_item_schema() { 'type' => 'string', 'context' => array( 'view', 'embed', 'edit' ), 'arg_options' => array( - 'sanitize_callback' => 'sanitize_title', + 'sanitize_callback' => array( $this, 'sanitize_slug' ), ), ), 'taxonomy' => array( diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 3abd438d1f..db03136e3a 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -802,7 +802,7 @@ public function get_item_schema() { 'type' => 'string', 'context' => array( 'embed', 'view', 'edit' ), 'arg_options' => array( - 'sanitize_callback' => 'sanitize_title', + 'sanitize_callback' => array( $this, 'sanitize_slug' ), ), ), 'registered_date' => array( From 879b8199bad7a454f7e01cdd208f52bbfab8ced5 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Fri, 23 Sep 2016 12:42:56 -0400 Subject: [PATCH 131/318] Improve the phpDoc for sanitize_slug --- lib/endpoints/class-wp-rest-controller.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 211a8f59b3..564addba72 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -516,13 +516,17 @@ public function get_post( $post ) { } /** - * Sanitize a post's title. + * Sanitize the slug value. * - * It's not possible to pass `sanitize_title` directly as the sanitize_callback - * as that will cause 3 params inadvertantly being passed to `sanitize_title`. + * @internal We can't use {@see sanitize_title} directly, as the second + * parameter is the fallback title, which would end up being set to the + * request object. + * @see https://github.com/WP-API/WP-API/issues/1585 * - * @param string $slug - * @return string + * @todo Remove this in favour of https://core.trac.wordpress.org/ticket/34659 + * + * @param string $slug Slug value passed in request. + * @return string Sanitized value for the slug. */ public function sanitize_slug( $slug ) { return sanitize_title( $slug ); From d92d62c2dfd648837d0b48c62dc5f50fec8409bb Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Fri, 23 Sep 2016 21:15:35 -0400 Subject: [PATCH 132/318] Call array_values() on user roles to ensure array is returned. #2582 was fixed in #2724, but it is also possible to remove user roles from an array in `set_user_role`, `add_user_role`, or `remove_user_role`. It would be pretty hard to do so, and most likely no one does it, but here is a PR just in case. --- lib/endpoints/class-wp-rest-users-controller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 4b9e2923b9..01c9d48090 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -552,7 +552,8 @@ public function prepare_item_for_response( $user, $request ) { } if ( ! empty( $schema['properties']['roles'] ) ) { - $data['roles'] = $user->roles; + // Defensively call array_values() to ensure an array is returned. + $data['roles'] = array_values( $user->roles ); } if ( ! empty( $schema['properties']['registered_date'] ) ) { From 69ea22f52f54bbfd923c3d3964a8e6ad6108748b Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Sun, 25 Sep 2016 10:09:17 -0400 Subject: [PATCH 133/318] Adding in missing update additional fields Adding in the missing update additional fields callback. Can also handle returning of errors for the additional callbacks. --- lib/endpoints/class-wp-rest-attachments-controller.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index 17520d4331..a98a8b3e63 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -181,6 +181,12 @@ public function update_item( $request ) { } $attachment = $this->get_post( $request['id'] ); + + $fields_update = $this->update_additional_fields_for_object( $attachment, $request ); + if ( is_wp_error( $fields_update ) ) { + return $fields_update; + } + $request->set_param( 'context', 'edit' ); $response = $this->prepare_item_for_response( $attachment, $request ); $response = rest_ensure_response( $response ); From b0dad329f927a8628ca3d255983690ade4f28e32 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Sun, 25 Sep 2016 10:30:09 -0400 Subject: [PATCH 134/318] Add tests for update extra field callback errors. Fixes #2729. Here are tests to show that the update callback can now handle returning errors for the response. The tests just add a returnError flag to the update callback and if that value is supplied an error will be returned. --- tests/test-rest-attachments-controller.php | 40 ++++++++++++++++++++++ tests/test-rest-comments-controller.php | 34 ++++++++++++++++++ tests/test-rest-posts-controller.php | 32 +++++++++++++++++ tests/test-rest-tags-controller.php | 36 +++++++++++++++++++ tests/test-rest-users-controller.php | 37 ++++++++++++++++++++ 5 files changed, 179 insertions(+) diff --git a/tests/test-rest-attachments-controller.php b/tests/test-rest-attachments-controller.php index f490769a16..0be44bb0ca 100644 --- a/tests/test-rest-attachments-controller.php +++ b/tests/test-rest-attachments-controller.php @@ -749,10 +749,50 @@ public function test_get_additional_field_registration() { $wp_rest_additional_fields = array(); } + public function test_additional_field_update_errors() { + $schema = array( + 'type' => 'integer', + 'description' => 'Some integer of mine', + 'enum' => array( 1, 2, 3, 4 ), + 'context' => array( 'view', 'edit' ), + ); + + register_rest_field( 'attachment', 'my_custom_int', array( + 'schema' => $schema, + 'get_callback' => array( $this, 'additional_field_get_callback' ), + 'update_callback' => array( $this, 'additional_field_update_callback' ), + ) ); + + wp_set_current_user( $this->editor_id ); + $attachment_id = $this->factory->attachment->create_object( $this->test_file, 0, array( + 'post_mime_type' => 'image/jpeg', + 'post_excerpt' => 'A sample caption', + 'post_author' => $this->editor_id, + ) ); + // Check for error on update. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/media/%d', $attachment_id ) ); + $request->set_body_params(array( + 'my_custom_int' => 'returnError', + )); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + global $wp_rest_additional_fields; + $wp_rest_additional_fields = array(); + } + public function additional_field_get_callback( $object, $request ) { return 123; } + public function additional_field_update_callback( $value, $attachment ) { + if ( 'returnError' === $value ) { + return new WP_Error( 'rest_invalid_param', 'Testing an error.', array( 'status' => 400 ) ); + } + } + public function tearDown() { parent::tearDown(); if ( file_exists( $this->test_file ) ) { diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 314586a7d6..e502b9015e 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1313,11 +1313,45 @@ public function test_get_additional_field_registration() { $wp_rest_additional_fields = array(); } + public function test_additional_field_update_errors() { + $schema = array( + 'type' => 'integer', + 'description' => 'Some integer of mine', + 'enum' => array( 1, 2, 3, 4 ), + 'context' => array( 'view', 'edit' ), + ); + + register_rest_field( 'comment', 'my_custom_int', array( + 'schema' => $schema, + 'get_callback' => array( $this, 'additional_field_get_callback' ), + 'update_callback' => array( $this, 'additional_field_update_callback' ), + ) ); + + wp_set_current_user( $this->admin_id ); + + // Check for error on update. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); + $request->set_body_params(array( + 'my_custom_int' => 'returnError', + 'content' => 'abc', + )); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + global $wp_rest_additional_fields; + $wp_rest_additional_fields = array(); + } + public function additional_field_get_callback( $object ) { return get_comment_meta( $object['id'], 'my_custom_int', true ); } public function additional_field_update_callback( $value, $comment ) { + if ( 'returnError' === $value ) { + return new WP_Error( 'rest_invalid_param', 'Testing an error.', array( 'status' => 400 ) ); + } update_comment_meta( $comment->comment_ID, 'my_custom_int', $value ); } diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 03bd834e95..fdba65b38f 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -1708,11 +1708,43 @@ public function test_get_additional_field_registration() { $wp_rest_additional_fields = array(); } + public function test_additional_field_update_errors() { + $schema = array( + 'type' => 'integer', + 'description' => 'Some integer of mine', + 'enum' => array( 1, 2, 3, 4 ), + 'context' => array( 'view', 'edit' ), + ); + + register_rest_field( 'post', 'my_custom_int', array( + 'schema' => $schema, + 'get_callback' => array( $this, 'additional_field_get_callback' ), + 'update_callback' => array( $this, 'additional_field_update_callback' ), + ) ); + + wp_set_current_user( $this->editor_id ); + // Check for error on update. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( array( + 'my_custom_int' => 'returnError', + ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + global $wp_rest_additional_fields; + $wp_rest_additional_fields = array(); + } + public function additional_field_get_callback( $object ) { return get_post_meta( $object['id'], 'my_custom_int', true ); } public function additional_field_update_callback( $value, $post ) { + if ( 'returnError' === $value ) { + return new WP_Error( 'rest_invalid_param', 'Testing an error.', array( 'status' => 400 ) ); + } update_post_meta( $post->ID, 'my_custom_int', $value ); } diff --git a/tests/test-rest-tags-controller.php b/tests/test-rest-tags-controller.php index 91a69a6e4a..6b7f663192 100644 --- a/tests/test-rest-tags-controller.php +++ b/tests/test-rest-tags-controller.php @@ -634,10 +634,46 @@ public function test_get_additional_field_registration() { $wp_rest_additional_fields = array(); } + public function test_additional_field_update_errors() { + $schema = array( + 'type' => 'integer', + 'description' => 'Some integer of mine', + 'enum' => array( 1, 2, 3, 4 ), + 'context' => array( 'view', 'edit' ), + ); + + register_rest_field( 'tag', 'my_custom_int', array( + 'schema' => $schema, + 'get_callback' => array( $this, 'additional_field_get_callback' ), + 'update_callback' => array( $this, 'additional_field_update_callback' ), + ) ); + + wp_set_current_user( $this->administrator ); + $tag_id = $this->factory->tag->create(); + // Check for error on update. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/tags/%d', $tag_id ) ); + $request->set_body_params( array( + 'my_custom_int' => 'returnError', + ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + global $wp_rest_additional_fields; + $wp_rest_additional_fields = array(); + } + public function additional_field_get_callback( $object, $request ) { return 123; } + public function additional_field_update_callback( $value, $tag ) { + if ( 'returnError' === $value ) { + return new WP_Error( 'rest_invalid_param', 'Testing an error.', array( 'status' => 400 ) ); + } + } + public function tearDown() { _unregister_taxonomy( 'batman' ); _unregister_taxonomy( 'robin' ); diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index c48176f34e..801cea0aac 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -1101,11 +1101,48 @@ public function test_get_additional_field_registration() { $wp_rest_additional_fields = array(); } + public function test_additional_field_update_errors() { + $schema = array( + 'type' => 'integer', + 'description' => 'Some integer of mine', + 'enum' => array( 1, 2, 3, 4 ), + 'context' => array( 'view', 'edit' ), + ); + + register_rest_field( 'user', 'my_custom_int', array( + 'schema' => $schema, + 'get_callback' => array( $this, 'additional_field_get_callback' ), + 'update_callback' => array( $this, 'additional_field_update_callback' ), + ) ); + + wp_set_current_user( 1 ); + if ( is_multisite() ) { + $current_user = wp_get_current_user( 1 ); + update_site_option( 'site_admins', array( $current_user->user_login ) ); + } + + // Check for error on update. + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d', $this->user ) ); + $request->set_body_params( array( + 'my_custom_int' => 'returnError', + ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + global $wp_rest_additional_fields; + $wp_rest_additional_fields = array(); + } + public function additional_field_get_callback( $object ) { return get_user_meta( $object['id'], 'my_custom_int', true ); } public function additional_field_update_callback( $value, $user ) { + if ( 'returnError' === $value ) { + return new WP_Error( 'rest_invalid_param', 'Testing an error.', array( 'status' => 400 ) ); + } update_user_meta( $user->ID, 'my_custom_int', $value ); } From bc20fa33eb9aa1dab9947168c57c3e854909ff60 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Mon, 26 Sep 2016 12:32:59 -0400 Subject: [PATCH 135/318] Move post_password_required filtering to preparation This allows resetting the filter after the content/excerpt have been generated, ensuring the endpoints are re-entrant. --- .../class-wp-rest-posts-controller.php | 61 ++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index a6479d72d5..6b890a885e 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -78,11 +78,6 @@ public function get_items_permissions_check( $request ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); } - // Allow access to all password protected posts if the context is edit. - if ( 'edit' === $request['context'] ) { - add_filter( 'post_password_required', '__return_false' ); - } - return true; } @@ -166,6 +161,11 @@ public function get_items( $request ) { $posts_query = new WP_Query(); $query_result = $posts_query->query( $query_args ); + // Allow access to all password protected posts if the context is edit. + if ( 'edit' === $request['context'] ) { + add_filter( 'post_password_required', '__return_false' ); + } + $posts = array(); foreach ( $query_result as $post ) { if ( ! $this->check_read_permission( $post ) ) { @@ -176,6 +176,11 @@ public function get_items( $request ) { $posts[] = $this->prepare_response_for_collection( $data ); } + // Reset filter. + if ( 'edit' === $request['context'] ) { + remove_filter( 'post_password_required', '__return_false' ); + } + $page = (int) $query_args['paged']; $total_posts = $posts_query->found_posts; @@ -233,11 +238,9 @@ public function get_item_permissions_check( $request ) { } if ( $post && ! empty( $request['password'] ) ) { + // Check post password, and return error if invalid. if ( ! hash_equals( $post->post_password, $request['password'] ) ) { return new WP_Error( 'rest_post_incorrect_password', __( 'Incorrect post password.' ), array( 'status' => 403 ) ); - } else { - // Allow access to the post going forward. - add_filter( 'post_password_required', '__return_false' ); } } @@ -253,6 +256,36 @@ public function get_item_permissions_check( $request ) { return true; } + /** + * Can the user access passworded content? + * + * This method determines whether we need to override the regular password + * check in core with a filter. + * + * @param WP_Post $post Post to check against. + * @param WP_REST_Request $request Request data to check. + * @return bool True if the user can access passworded content, false otherwise. + */ + protected function can_access_password_content( $post, $request ) { + if ( empty( $post->post_password ) ) { + // No filter required. + return false; + } + + // Edit context always gets access to passworded posts. + if ( $request['context'] === 'edit' ) { + return true; + } + + // No password, no auth. + if ( empty( $request['password'] ) ) { + return false; + } + + // Double-check the request password. + return hash_equals( $post->post_password, $request['password'] ); + } + /** * Get a single post. * @@ -1119,6 +1152,13 @@ public function prepare_item_for_response( $post, $request ) { remove_filter( 'protected_title_format', array( $this, 'protected_title_format' ) ); } + $has_password_filter = false; + if ( $this->can_access_password_content( $post, $request ) ) { + // Allow access to the post, permissions already checked before. + add_filter( 'post_password_required', '__return_false' ); + $has_password_filter = true; + } + if ( ! empty( $schema['properties']['content'] ) ) { $data['content'] = array( 'raw' => $post->post_content, @@ -1138,6 +1178,11 @@ public function prepare_item_for_response( $post, $request ) { ); } + if ( $has_password_filter ) { + // Reset filter. + remove_filter( 'post_password_required', '__return_false' ); + } + if ( ! empty( $schema['properties']['author'] ) ) { $data['author'] = (int) $post->post_author; } From ebe34b303699fe09cd7c024e411ce62a8b32bd82 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 28 Sep 2016 10:57:12 -0400 Subject: [PATCH 136/318] Don't allow ordering by registered_date as it's not public. --- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 324ff10807..391916e259 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -86,7 +86,7 @@ public function get_items_permissions_check( $request ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view this resource with edit context.' ), array( 'status' => rest_authorization_required_code() ) ); } - if ( in_array( $request['orderby'], array( 'email' ), true ) && ! current_user_can( 'list_users' ) ) { + if ( in_array( $request['orderby'], array( 'email', 'registered_date' ), true ) && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_forbidden_orderby', __( 'Sorry, you cannot order by this parameter.' ), array( 'status' => rest_authorization_required_code() ) ); } From 3d60d9480ebc6cf675fd8d9e7b272abb337343f7 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 28 Sep 2016 10:57:26 -0400 Subject: [PATCH 137/318] unit tests for registered date and fix typeo --- tests/test-rest-users-controller.php | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index b9f32b0d84..1b18fafac4 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -336,22 +336,18 @@ public function test_get_items_orderby_email() { $this->assertEquals( $low_id, $data[0]['id'] ); } - public function test_get_items_orderby_email_unauthenticaed() { - $high_id = $this->factory->user->create( array( 'user_email' => 'bemail@gmail.com' ) ); - $low_id = $this->factory->user->create( array( 'user_email' => 'aemail@gmail.com' ) ); - - $this->factory->post->create( array( - 'post_author' => $high_id, - )); - $this->factory->post->create( array( - 'post_author' => $low_id, - )); - + public function test_get_items_orderby_email_unauthenticated() { $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); $request->set_param( 'orderby', 'email' ); $request->set_param( 'order', 'desc' ); - $request->set_param( 'per_page', 1 ); - $request->set_param( 'include', array( $low_id, $high_id ) ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_forbidden_orderby', $response, 401 ); + } + + public function test_get_items_orderby_registered_date_unauthenticated() { + $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); + $request->set_param( 'orderby', 'registered_date' ); + $request->set_param( 'order', 'desc' ); $response = $this->server->dispatch( $request ); $this->assertErrorResponse( 'rest_forbidden_orderby', $response, 401 ); } From 35cd7862c68be656243fccf7f826d4c8b4df903a Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 28 Sep 2016 11:39:59 -0400 Subject: [PATCH 138/318] Introduce rest_parse_request_arg() wrapper to perform a validation and sanitization in one step. --- plugin.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/plugin.php b/plugin.php index d54230360d..803180b6d2 100755 --- a/plugin.php +++ b/plugin.php @@ -414,6 +414,33 @@ function rest_sanitize_request_arg( $value, $request, $param ) { } } + +if ( ! function_exists( 'rest_parse_request_arg' ) ) { + /** + * Parse a request argument based on details registered to the route. + * + * Runs a validation check and sanitizes the value, primarily to be used via + * the `sanitize_callback` arguments in the endpoint args registration. + * + * @param mixed $value + * @param WP_REST_Request $request + * @param string $param + * @return mixed + */ + function rest_parse_request_arg( $value, $request, $param ) { + + $is_valid = rest_validate_request_arg( $value, $request, $param ); + + if ( is_wp_error( $is_valid ) ) { + return $is_valid; + } + + $value = rest_sanitize_request_arg( $value, $request, $param ); + + return $value; + } +} + if ( ! function_exists( 'rest_is_ip_address' ) ) { /** * Determines if a IPv4 address is valid. From 213f7aae90a45b373019c2e338fb6926eac1375c Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 28 Sep 2016 11:40:37 -0400 Subject: [PATCH 139/318] add `sanitize_callback` to the sticky param to allow passing bool-like values. --- lib/endpoints/class-wp-rest-posts-controller.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 5c525ee20e..dd475817be 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1801,8 +1801,9 @@ public function get_collection_params() { if ( 'post' === $this->post_type ) { $params['sticky'] = array( - 'description' => __( 'Limit result set to items that are sticky.' ), - 'type' => 'boolean', + 'description' => __( 'Limit result set to items that are sticky.' ), + 'type' => 'boolean', + 'sanitize_callback' => 'rest_parse_request_arg', ); } From 8dd445117bc26dcd9213e0dfb38f05228a975fd8 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 28 Sep 2016 11:52:01 -0400 Subject: [PATCH 140/318] Initial Settings Endpoint Merged in from https://github.com/WP-API/wp-api-site-endpoints. The bulk of this comes from https://github.com/WP-API/wp-api-site-endpoints/pull/13 --- .../class-wp-rest-settings-controller.php | 177 ++++++++++++++++++ plugin.php | 124 ++++++++++++ tests/test-rest-settings-controller.php | 154 +++++++++++++++ 3 files changed, 455 insertions(+) create mode 100644 lib/endpoints/class-wp-rest-settings-controller.php create mode 100644 tests/test-rest-settings-controller.php diff --git a/lib/endpoints/class-wp-rest-settings-controller.php b/lib/endpoints/class-wp-rest-settings-controller.php new file mode 100644 index 0000000000..fb16d25151 --- /dev/null +++ b/lib/endpoints/class-wp-rest-settings-controller.php @@ -0,0 +1,177 @@ +namespace, '/' . $this->rest_base, array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'args' => array(), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_item' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) ); + } + + /** + * Check if a given request has access to read and manage settings. + * + * @param WP_REST_Request $request Full details about the request. + * @return boolean + */ + public function get_item_permissions_check( $request ) { + return current_user_can( 'manage_options' ); + } + + /** + * Get the settings. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|array + */ + public function get_item( $request ) { + $options = $this->get_registered_options(); + $response = array(); + + foreach ( $options as $name => $args ) { + // Default to a null value as "null" in the response means "not set". + $response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] ); + + // Because get_option() is lossy, we have to + // cast values to the type they are registered with. + $response[ $name ] = $this->cast_value_to_type( $response[ $name ], $args['schema']['type'] ); + } + + return $response; + } + + /** + * Convert a value to a given schema type. + * + * @param mixed $value + * @param string $type Type that the data should be converted to. + * @return mixed + */ + protected function cast_value_to_type( $value, $type ) { + switch ( $type ) { + case 'string': + return strval( $value ); + case 'number': + return floatval( $value ); + case 'boolean': + return (bool) $value; + default: + return $value; + } + } + + /** + * Update settings for the settings object. + * + * @param WP_REST_Request $request Full detail about the request. + * @return WP_Error|array + */ + public function update_item( $request ) { + $options = $this->get_registered_options(); + $params = $request->get_params(); + + foreach ( $options as $name => $args ) { + if ( ! array_key_exists( $name, $params ) ) { + continue; + } + // A null value means reset the option, which is essentially deleting it + // from the database and then relying on the default value. + if ( is_null( $request[ $name ] ) ) { + delete_option( $args['option_name'] ); + } else { + update_option( $args['option_name'], $request[ $name ] ); + } + } + + return $this->get_item( $request ); + } + + /** + * Get all the registered options for the Settings API + * + * @return array + */ + protected function get_registered_options() { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + $rest_options = array(); + + foreach ( get_registered_settings() as $name => $args ) { + if ( empty( $args['show_in_rest'] ) ) { + continue; + } + + $rest_args = array(); + if ( is_array( $args['show_in_rest'] ) ) { + $rest_args = $args['show_in_rest']; + } + + $defaults = array( + 'name' => ! empty( $rest_args['name'] ) ? $rest_args['name'] : $name, + 'schema' => array(), + ); + $rest_args = array_merge( $defaults, $rest_args ); + + $default_schema = array( + 'type' => empty( $args['type'] ) ? null : $args['type'], + 'description' => empty( $args['description'] ) ? '' : $args['description'], + 'default' => isset( $args['default'] ) ? $args['default'] : null, + ); + + $rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] ); + $rest_args['option_name'] = $name; + + // Skip over settings that don't have a defined type in the schema. + if ( empty( $rest_args['schema']['type'] ) ) { + continue; + } + + $rest_options[ $rest_args['name'] ] = $rest_args; + } + + return $rest_options; + } + + /** + * Get the site setting schema, conforming to JSON Schema. + * + * @return array + */ + public function get_item_schema() { + $options = $this->get_registered_options(); + + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'settings', + 'type' => 'object', + 'properties' => array(), + ); + + foreach ( $options as $option_name => $option ) { + $schema['properties'][ $option_name ] = $option['schema']; + } + + return $this->add_additional_fields_schema( $schema ); + } +} diff --git a/plugin.php b/plugin.php index d54230360d..72d638a30a 100755 --- a/plugin.php +++ b/plugin.php @@ -79,6 +79,13 @@ require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-comments-controller.php'; } +/** + * WP_REST_Settings_Controller class. + */ +if ( ! class_exists( 'WP_REST_Settings_Controller' ) ) { + require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-settings-controller.php'; +} + /** * REST extras. */ @@ -87,6 +94,7 @@ add_filter( 'init', '_add_extra_api_post_type_arguments', 11 ); add_action( 'init', '_add_extra_api_taxonomy_arguments', 11 ); +add_action( 'rest_api_init', 'rest_register_settings', 0 ); add_action( 'rest_api_init', 'create_initial_rest_routes', 0 ); /** @@ -145,6 +153,118 @@ function _add_extra_api_taxonomy_arguments() { } } + +/** + * Register the settings to be used in the REST API. + * + * This is required are WordPress Core does not internally register + * it's settings via `register_rest_setting()`. This should be removed + * once / if core starts to register settings internally. + */ +function rest_register_settings() { + require_once ABSPATH . 'wp-admin/includes/plugin.php'; + + register_setting( 'general', 'blogname', array( + 'show_in_rest' => array( + 'name' => 'title', + ), + 'type' => 'string', + 'description' => __( 'Site title.' ), + ) ); + + register_setting( 'general', 'blogdescription', array( + 'show_in_rest' => array( + 'name' => 'description', + ), + 'type' => 'string', + 'description' => __( 'Site description.' ), + ) ); + + register_setting( 'general', 'siteurl', array( + 'show_in_rest' => array( + 'name' => 'url', + 'schema' => array( + 'format' => 'uri', + ), + ), + 'type' => 'string', + 'description' => __( 'Site URL' ), + ) ); + + register_setting( 'general', 'admin_email', array( + 'show_in_rest' => array( + 'name' => 'email', + 'schema' => array( + 'format' => 'email', + ), + ), + 'type' => 'string', + 'description' => __( 'This address is used for admin purposes. If you change this we will send you an email at your new address to confirm it. The new address will not become active until confirmed.' ), + ) ); + + register_setting( 'general', 'timezone_string', array( + 'show_in_rest' => array( + 'name' => 'timezone', + ), + 'type' => 'string', + 'description' => __( 'A city in the same timezone as you.' ), + ) ); + + register_setting( 'general', 'date_format', array( + 'show_in_rest' => true, + 'type' => 'string', + 'description' => __( 'A date format for all date strings.' ), + ) ); + + register_setting( 'general', 'time_format', array( + 'show_in_rest' => true, + 'type' => 'string', + 'description' => __( 'A time format for all time strings.' ), + ) ); + + register_setting( 'general', 'start_of_week', array( + 'show_in_rest' => true, + 'type' => 'number', + 'description' => __( 'A day number of the week that the week should start on.' ), + ) ); + + register_setting( 'general', 'WPLANG', array( + 'show_in_rest' => array( + 'name' => 'language', + ), + 'type' => 'string', + 'description' => __( 'WordPress locale code.' ), + 'default' => 'en_US', + ) ); + + register_setting( 'writing', 'use_smilies', array( + 'show_in_rest' => true, + 'type' => 'boolean', + 'description' => __( 'Convert emoticons like :-) and :-P to graphics on display.' ), + 'default' => true, + ) ); + + register_setting( 'writing', 'default_category', array( + 'show_in_rest' => true, + 'type' => 'number', + 'description' => __( 'Default category.' ), + ) ); + + register_setting( 'writing', 'default_post_format', array( + 'show_in_rest' => true, + 'type' => 'string', + 'description' => __( 'Default post format.' ), + ) ); + + register_setting( 'reading', 'posts_per_page', array( + 'show_in_rest' => true, + 'type' => 'number', + 'description' => __( 'Blog pages show at most.' ), + 'default' => 10, + ) ); +} + + if ( ! function_exists( 'create_initial_rest_routes' ) ) { /** * Registers default REST API routes. @@ -206,6 +326,10 @@ function create_initial_rest_routes() { // Comments. $controller = new WP_REST_Comments_Controller; $controller->register_routes(); + + // Settings. + $controller = new WP_REST_Settings_Controller; + $controller->register_routes(); } } diff --git a/tests/test-rest-settings-controller.php b/tests/test-rest-settings-controller.php new file mode 100644 index 0000000000..96ada97df9 --- /dev/null +++ b/tests/test-rest-settings-controller.php @@ -0,0 +1,154 @@ +markTestSkipped( 'WordPress version not supported.' ); + } + parent::setUp(); + $this->administrator = $this->factory->user->create( array( + 'role' => 'administrator', + ) ); + $this->endpoint = new WP_REST_Settings_Controller(); + } + + public function test_register_routes() { + $routes = $this->server->get_routes(); + $this->assertArrayHasKey( '/wp/v2/settings', $routes ); + } + + public function test_get_items() { + } + + public function test_context_param() { + } + + public function test_get_item_is_not_public() { + $request = new WP_REST_Request( 'GET', '/wp/v2/settings' ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 403, $response->get_status() ); + } + + public function test_get_item() { + wp_set_current_user( $this->administrator ); + $request = new WP_REST_Request( 'GET', '/wp/v2/settings' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( array( + 'title', + 'description', + 'url', + 'email', + 'timezone', + 'date_format', + 'time_format', + 'start_of_week', + 'language', + 'use_smilies', + 'default_category', + 'default_post_format', + 'posts_per_page', + ), array_keys( $data ) ); + } + + public function test_get_item_value_is_cast_to_type() { + wp_set_current_user( $this->administrator ); + update_option( 'posts_per_page', 'invalid_number' ); // this is cast to (int) 1 + $request = new WP_REST_Request( 'GET', '/wp/v2/settings' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 1, $data['posts_per_page'] ); + } + + public function test_get_item_with_custom_setting() { + wp_set_current_user( $this->administrator ); + + register_setting( 'somegroup', 'mycustomsetting', array( + 'show_in_rest' => array( + 'name' => 'mycustomsettinginrest', + 'schema' => array( + 'enum' => array( 'validvalue1', 'validvalue2' ), + 'default' => 'validvalue1', + ), + ), + 'type' => 'string', + ) ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/settings' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertArrayHasKey( 'mycustomsettinginrest', $data ); + $this->assertEquals( 'validvalue1', $data['mycustomsettinginrest'] ); + + update_option( 'mycustomsetting', 'validvalue2' ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/settings' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 'validvalue2', $data['mycustomsettinginrest'] ); + + unregister_setting( 'somegroup', 'mycustomsetting' ); + } + + public function test_create_item() { + } + + public function test_update_item() { + wp_set_current_user( $this->administrator ); + $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' ); + $request->set_param( 'title', 'The new title!' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'The new title!', $data['title'] ); + $this->assertEquals( get_option( 'blogname' ), $data['title'] ); + } + + public function test_update_item_with_invalid_type() { + wp_set_current_user( $this->administrator ); + $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' ); + $request->set_param( 'title', array( 'rendered' => 'This should fail.' ) ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + + /** + * Setting an item to "null" will essentially restore it to it's default value. + */ + public function test_update_item_with_null() { + update_option( 'posts_per_page', 9 ); + + wp_set_current_user( $this->administrator ); + $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' ); + $request->set_param( 'posts_per_page', null ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 10, $data['posts_per_page'] ); + } + + public function test_delete_item() { + } + + public function test_prepare_item() { + } + + public function test_get_item_schema() { + } +} From 6090e7207c82fb9f7b0613a58f9db1919cb3a43f Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Wed, 28 Sep 2016 14:34:42 -0400 Subject: [PATCH 141/318] Fixing Indentation Thank you @rachelbaker that was a good catch. The indentation was off from when embeddable parameter was removed. This patch fixes the spacing. --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 8f83ae2365..1321281fcf 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -653,7 +653,7 @@ protected function prepare_links( $comment ) { $rest_url = add_query_arg( $args, rest_url( $this->namespace . '/' . $this->rest_base ) ); $links['children'] = array( - 'href' => $rest_url, + 'href' => $rest_url, ); } From b3b934f342a7fb43b4d0c6e5531f24c1bf8ef0cb Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 28 Sep 2016 16:43:41 -0400 Subject: [PATCH 142/318] Fix tests for pages and attachments as they don't have the `password` field anymore. --- tests/test-rest-attachments-controller.php | 2 +- tests/test-rest-pages-controller.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-rest-attachments-controller.php b/tests/test-rest-attachments-controller.php index f490769a16..95ff19c769 100644 --- a/tests/test-rest-attachments-controller.php +++ b/tests/test-rest-attachments-controller.php @@ -688,7 +688,7 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 23, count( $properties ) ); + $this->assertEquals( 22, count( $properties ) ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'alt_text', $properties ); $this->assertArrayHasKey( 'caption', $properties ); diff --git a/tests/test-rest-pages-controller.php b/tests/test-rest-pages-controller.php index 985fccf297..beaec6cb9f 100644 --- a/tests/test-rest-pages-controller.php +++ b/tests/test-rest-pages-controller.php @@ -359,7 +359,7 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 21, count( $properties ) ); + $this->assertEquals( 20, count( $properties ) ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'comment_status', $properties ); $this->assertArrayHasKey( 'content', $properties ); From 55ed6bbbad60fdaf0d6cc3499e3d2be6b5ace4a9 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 28 Sep 2016 16:52:05 -0400 Subject: [PATCH 143/318] We don't support password on pages and attachments no mo' --- tests/test-rest-attachments-controller.php | 1 - tests/test-rest-pages-controller.php | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/test-rest-attachments-controller.php b/tests/test-rest-attachments-controller.php index 95ff19c769..b1225f183c 100644 --- a/tests/test-rest-attachments-controller.php +++ b/tests/test-rest-attachments-controller.php @@ -704,7 +704,6 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'media_details', $properties ); $this->assertArrayHasKey( 'modified', $properties ); $this->assertArrayHasKey( 'modified_gmt', $properties ); - $this->assertArrayHasKey( 'password', $properties ); $this->assertArrayHasKey( 'post', $properties ); $this->assertArrayHasKey( 'ping_status', $properties ); $this->assertArrayHasKey( 'status', $properties ); diff --git a/tests/test-rest-pages-controller.php b/tests/test-rest-pages-controller.php index beaec6cb9f..bd6a73bb84 100644 --- a/tests/test-rest-pages-controller.php +++ b/tests/test-rest-pages-controller.php @@ -374,7 +374,6 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'modified', $properties ); $this->assertArrayHasKey( 'modified_gmt', $properties ); $this->assertArrayHasKey( 'parent', $properties ); - $this->assertArrayHasKey( 'password', $properties ); $this->assertArrayHasKey( 'ping_status', $properties ); $this->assertArrayHasKey( 'slug', $properties ); $this->assertArrayHasKey( 'status', $properties ); From de8dd1a1445742fb39d3900b78024fc92b79a0d8 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 28 Sep 2016 17:02:24 -0400 Subject: [PATCH 144/318] Skip the test on < 4.7 --- tests/test-rest-posts-controller.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index aa68f45871..e3faf81e61 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -616,6 +616,11 @@ public function test_get_post_with_password() { } public function test_get_post_with_password_using_password() { + global $wp_version; + if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { + return $this->markTestSkipped( 'WordPress < 4.6 does not support filtering passwords for posts.' ); + } + $post_id = $this->factory->post->create( array( 'post_password' => '$inthebananastand', 'post_content' => 'Some secret content.', From 0795ae507b7a94fa85b9e9d7dccd3fce38091958 Mon Sep 17 00:00:00 2001 From: Patrick Galbraith Date: Thu, 29 Sep 2016 10:37:12 +0930 Subject: [PATCH 145/318] Remove unnecessary email validation check --- lib/endpoints/class-wp-rest-comments-controller.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 91166f86e6..7a81489fb7 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -189,8 +189,6 @@ public function create_item( $request ) { if ( get_option( 'require_name_email' ) && ! isset( $user ) ) { if ( empty( $prepared_comment['comment_author_email'] ) || empty( $prepared_comment['comment_author'] ) ) { return new WP_Error( 'rest_require_valid_comment', __( 'Required fields (name, email) missing.' ), array( 'status' => 400 ) ); - } elseif ( ! is_email( $prepared_comment['comment_author_email'] ) ) { - return new WP_Error( 'rest_require_valid_comment', __( 'Valid email address required.' ), array( 'status' => 400 ) ); } } From 6282db4cb06e7e825d7767b837d008f9cd33a012 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 28 Sep 2016 21:50:16 -0500 Subject: [PATCH 146/318] Require comment content on create and update to match core comment handling --- lib/endpoints/class-wp-rest-comments-controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 37b4807b79..fd48504a9d 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -318,6 +318,10 @@ public function create_item( $request ) { return new WP_Error( 'rest_comment_exists', __( 'Cannot create existing comment.' ), array( 'status' => 400 ) ); } + if ( empty( $request['content'] ) || ! is_string( $request['content'] ) ) { + return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); + } + $prepared_comment = $this->prepare_item_for_database( $request ); // Setting remaining values before wp_insert_comment so we can @@ -443,6 +447,10 @@ public function update_item( $request ) { } else { $prepared_args['comment_ID'] = $id; + if ( empty( $request['content'] ) || ! is_string( $request['content'] ) ) { + return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); + } + $updated = wp_update_comment( $prepared_args ); if ( 0 === $updated ) { return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment failed.' ), array( 'status' => 500 ) ); From 3197f8ea674c134fc6b142eb2dd005cd75c72396 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 28 Sep 2016 21:50:59 -0500 Subject: [PATCH 147/318] Add unit tests for missing comment content --- tests/test-rest-comments-controller.php | 35 ++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index e502b9015e..4d9eaee4ef 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -641,6 +641,25 @@ public function test_create_item_invalid_date() { $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } + public function test_create_item_invalid_content() { + wp_set_current_user( 0 ); + + $params = array( + 'post' => $this->post_id, + 'author_name' => 'Reverend Lovejoy', + 'author_email' => 'lovejoy@example.com', + 'author_url' => 'http://timothylovejoy.jr', + 'content' => '', + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_comment_content_required', $response, 400 ); + } + public function test_create_item_assign_different_user() { $subscriber_id = $this->factory->user->create( array( 'role' => 'subscriber', @@ -1069,6 +1088,7 @@ public function test_update_comment_date_gmt() { wp_set_current_user( $this->admin_id ); $params = array( + 'content' => rand_str(), 'date_gmt' => '2015-05-07T10:14:25', ); $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); @@ -1088,7 +1108,8 @@ public function test_update_comment_invalid_type() { wp_set_current_user( $this->admin_id ); $params = array( - 'type' => 'trackback', + 'type' => 'trackback', + 'content' => rand_str(), ); $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); $request->add_header( 'content-type', 'application/json' ); @@ -1098,6 +1119,18 @@ public function test_update_comment_invalid_type() { $this->assertErrorResponse( 'rest_comment_invalid_type', $response, 404 ); } + public function test_update_comment_invalid_content() { + wp_set_current_user( $this->admin_id ); + + $params = array(); + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_comment_content_required', $response, 400 ); + } + public function test_update_item_invalid_date() { wp_set_current_user( $this->admin_id ); From d91e28b34c36e379679a33dd305b28c398522f84 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 28 Sep 2016 22:07:31 -0500 Subject: [PATCH 148/318] Allow comment content to be empty on update, but if set confirm it is a string --- lib/endpoints/class-wp-rest-comments-controller.php | 8 ++++---- tests/test-rest-comments-controller.php | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index fd48504a9d..b7f9a938f7 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -436,6 +436,10 @@ public function update_item( $request ) { return new WP_Error( 'rest_comment_invalid_type', __( 'Sorry, you cannot change the comment type.' ), array( 'status' => 404 ) ); } + if ( isset( $request['content'] ) && ! is_string( $request['content'] ) ) { + return new WP_Error( 'rest_comment_content_invalid', __( 'Invalid comment content.' ), array( 'status' => 400 ) ); + } + $prepared_args = $this->prepare_item_for_database( $request ); if ( empty( $prepared_args ) && isset( $request['status'] ) ) { @@ -447,10 +451,6 @@ public function update_item( $request ) { } else { $prepared_args['comment_ID'] = $id; - if ( empty( $request['content'] ) || ! is_string( $request['content'] ) ) { - return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); - } - $updated = wp_update_comment( $prepared_args ); if ( 0 === $updated ) { return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment failed.' ), array( 'status' => 500 ) ); diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 4d9eaee4ef..db64327a64 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1122,13 +1122,15 @@ public function test_update_comment_invalid_type() { public function test_update_comment_invalid_content() { wp_set_current_user( $this->admin_id ); - $params = array(); + $params = array( + 'content' => array( 1, 2, 3 ), + ); $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); $request->add_header( 'content-type', 'application/json' ); $request->set_body( wp_json_encode( $params ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_comment_content_required', $response, 400 ); + $this->assertErrorResponse( 'rest_comment_content_invalid', $response, 400 ); } public function test_update_item_invalid_date() { From e1b0d916b218dd21f6756fad523165b97ba311bf Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 28 Sep 2016 22:16:12 -0500 Subject: [PATCH 149/318] Remove extraneous content params added to a select update comment unit tests. Originally added in 3197f8ea674c134fc6b142eb2dd005cd75c72396 --- tests/test-rest-comments-controller.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index db64327a64..10363ef0b5 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1088,7 +1088,6 @@ public function test_update_comment_date_gmt() { wp_set_current_user( $this->admin_id ); $params = array( - 'content' => rand_str(), 'date_gmt' => '2015-05-07T10:14:25', ); $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); @@ -1108,8 +1107,7 @@ public function test_update_comment_invalid_type() { wp_set_current_user( $this->admin_id ); $params = array( - 'type' => 'trackback', - 'content' => rand_str(), + 'type' => 'trackback', ); $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); $request->add_header( 'content-type', 'application/json' ); From d27fbb4bc0aa1cfda7d1c4a329fda96df06816f5 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 29 Sep 2016 12:12:43 -0400 Subject: [PATCH 150/318] Add a update_item_permissions_check callback to class-wp-rest-attachments-controller.php, matching core cap checks --- .../class-wp-rest-attachments-controller.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index df7718bb66..3548d1e1ae 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -57,6 +57,35 @@ public function create_item_permissions_check( $request ) { return true; } + /** + * Check if a given request has access to update an attachment. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|boolean + */ + public function update_item_permissions_check( $request ) { + + $item = $this->get_post( (int) $request['id'] ); + if ( ! $item ) { + return false; + } + + // Only attachments can be edited by the attachments controller. + if ( 'attachment' !== $item->post_type ) { + return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this item.' ), array( 'status' => rest_authorization_required_code() ) ); + } + + if ( ! current_user_can( 'edit_post', (int) $request['id'] ) ) { + return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to update this item.' ), array( 'status' => rest_authorization_required_code() ) ); + } + + if ( 'trash' === $item->post_status ) { + return new WP_Error( 'rest_cannot_edit', __( 'You can’t edit this attachment because it is in the Trash. Please move it out of the Trash and try again.' ), array( 'status' => 410 ) ); + } + + return true; + } + /** * Create a single attachment * From fbe740d04e63e9724c1b05ae401f00ed03a7be7f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 29 Sep 2016 12:13:02 -0400 Subject: [PATCH 151/318] Fix up unit tests --- tests/test-rest-attachments-controller.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test-rest-attachments-controller.php b/tests/test-rest-attachments-controller.php index 4b47ea2186..5605e5c46c 100644 --- a/tests/test-rest-attachments-controller.php +++ b/tests/test-rest-attachments-controller.php @@ -20,14 +20,13 @@ public function setUp() { $this->contributor_id = $this->factory->user->create( array( 'role' => 'contributor', ) ); - //matches core capabilities for subscriber role and adds upload_files - _x( 'File upload role', 'User role' ); + + // Add an uploader role to test upload capabilities. add_role( 'uploader', 'File upload role' ); $role = get_role( 'uploader' ); $role->add_cap( 'upload_files' ); $role->add_cap( 'read' ); $role->add_cap( 'level_0' ); - //print_r(array('role'=>$role)); $this->uploader_id = $this->factory->user->create( array( 'role' => 'uploader', ) ); From 5da39e80d60dd01449d2d18366313f9290bce852 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 29 Sep 2016 12:44:57 -0400 Subject: [PATCH 152/318] =?UTF-8?q?Remove=20update=5Fitem=5Fpermissions=5F?= =?UTF-8?q?check=20in=20favor=20of=20parent=E2=80=99s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../class-wp-rest-attachments-controller.php | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index 3548d1e1ae..df7718bb66 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -57,35 +57,6 @@ public function create_item_permissions_check( $request ) { return true; } - /** - * Check if a given request has access to update an attachment. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean - */ - public function update_item_permissions_check( $request ) { - - $item = $this->get_post( (int) $request['id'] ); - if ( ! $item ) { - return false; - } - - // Only attachments can be edited by the attachments controller. - if ( 'attachment' !== $item->post_type ) { - return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this item.' ), array( 'status' => rest_authorization_required_code() ) ); - } - - if ( ! current_user_can( 'edit_post', (int) $request['id'] ) ) { - return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to update this item.' ), array( 'status' => rest_authorization_required_code() ) ); - } - - if ( 'trash' === $item->post_status ) { - return new WP_Error( 'rest_cannot_edit', __( 'You can’t edit this attachment because it is in the Trash. Please move it out of the Trash and try again.' ), array( 'status' => 410 ) ); - } - - return true; - } - /** * Create a single attachment * From 44d86d7a5862dcf2bf7a0210f01682995c40f44e Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 30 Sep 2016 14:14:05 -0400 Subject: [PATCH 153/318] Fix yoda condition, I must. --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 6ca6f216ca..6759065751 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -295,7 +295,7 @@ protected function can_access_password_content( $post, $request ) { } // Edit context always gets access to passworded posts. - if ( $request['context'] === 'edit' ) { + if ( 'edit' === $request['context'] ) { return true; } From 094a61824bace531bfcf84922d61cb1e856651b0 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 30 Sep 2016 18:15:28 -0400 Subject: [PATCH 154/318] Don't allow reading / creating of posts with no parent This also stops access to comments on private posts when accessing them directly. --- .../class-wp-rest-comments-controller.php | 21 +-- tests/test-rest-comments-controller.php | 150 ++++++++++++++++++ 2 files changed, 161 insertions(+), 10 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 1321281fcf..35da2e0d03 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -293,6 +293,10 @@ public function create_item_permissions_check( $request ) { return new WP_Error( 'rest_comment_invalid_status', __( 'Sorry, you cannot set status for comments.' ), array( 'status' => rest_authorization_required_code() ) ); } + if ( empty( $request['post'] ) && ! current_user_can( 'moderate_comments' ) ) { + return new WP_Error( 'rest_comment_invalid_post_id', __( 'Sorry, you cannot create this comment without a post' ), array( 'status' => rest_authorization_required_code() ) ); + } + if ( ! empty( $request['post'] ) && $post = $this->get_post( (int) $request['post'] ) ) { if ( ! $this->check_read_post_permission( $post ) ) { @@ -1148,9 +1152,13 @@ protected function check_read_post_permission( $post ) { * @return boolean Can we read it? */ protected function check_read_permission( $comment ) { - - if ( 1 === (int) $comment->comment_approved ) { - return true; + if ( ! empty( $comment->comment_post_ID ) ) { + $post = get_post( $comment->comment_post_ID ); + if ( $post ) { + if ( $this->check_read_post_permission( $post ) && 1 === (int) $comment->comment_approved ) { + return true; + } + } } if ( 0 === get_current_user_id() ) { @@ -1161,13 +1169,6 @@ protected function check_read_permission( $comment ) { return false; } - $post = $this->get_post( $comment->comment_post_ID ); - if ( $comment->comment_post_ID && $post ) { - if ( ! $this->check_read_post_permission( $post ) ) { - return false; - } - } - if ( ! empty( $comment->user_id ) && get_current_user_id() === (int) $comment->user_id ) { return true; } diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 1b3fc0fc2b..4dbb3d0733 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -12,6 +12,7 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase protected $subscriber_id; protected $post_id; + protected $private_id; protected $approved_id; protected $hold_id; @@ -32,6 +33,9 @@ public function setUp() { )); $this->post_id = $this->factory->post->create(); + $this->private_id = $this->factory->post->create( array( + 'post_status' => 'private', + )); $this->approved_id = $this->factory->comment->create( array( 'comment_approved' => 1, @@ -118,6 +122,80 @@ public function test_get_items() { $this->assertCount( 7, $comments ); } + public function test_get_items_without_private_post_permission() { + wp_set_current_user( 0 ); + + $args = array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->private_id, + ); + $private_comment = $this->factory->comment->create( $args ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $collection_data = $response->get_data(); + $this->assertFalse( in_array( $private_comment, wp_list_pluck( $collection_data, 'id' ) ) ); + } + + public function test_get_items_with_private_post_permission() { + wp_set_current_user( $this->admin_id ); + + $args = array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->private_id, + ); + $private_comment = $this->factory->comment->create( $args ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $collection_data = $response->get_data(); + $this->assertTrue( in_array( $private_comment, wp_list_pluck( $collection_data, 'id' ) ) ); + } + + public function test_get_items_with_invalid_post() { + wp_set_current_user( 0 ); + + $comment_id = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => REST_TESTS_IMPOSSIBLY_HIGH_NUMBER, + )); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $collection_data = $response->get_data(); + $this->assertFalse( in_array( $comment_id, wp_list_pluck( $collection_data, 'id' ) ) ); + + wp_delete_comment( $comment_id ); + } + + public function test_get_items_with_invalid_post_permission() { + wp_set_current_user( $this->admin_id ); + + $comment_id = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => REST_TESTS_IMPOSSIBLY_HIGH_NUMBER, + )); + + $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $collection_data = $response->get_data(); + $this->assertTrue( in_array( $comment_id, wp_list_pluck( $collection_data, 'id' ) ) ); + + wp_delete_comment( $comment_id ); + } + public function test_get_items_no_permission_for_context() { wp_set_current_user( 0 ); $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); @@ -567,6 +645,19 @@ public function test_get_comment_invalid_context() { } public function test_get_comment_invalid_post_id() { + wp_set_current_user( 0 ); + $comment_id = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => REST_TESTS_IMPOSSIBLY_HIGH_NUMBER, + )); + $request = new WP_REST_Request( 'GET', '/wp/v2/comments/' . $comment_id ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_read', $response, 401 ); + } + + public function test_get_comment_invalid_post_id_as_admin() { + wp_set_current_user( $this->admin_id ); $comment_id = $this->factory->comment->create( array( 'comment_approved' => 1, 'comment_post_ID' => REST_TESTS_IMPOSSIBLY_HIGH_NUMBER, @@ -917,6 +1008,45 @@ public function test_create_comment_no_post_id() { $this->assertEquals( 201, $response->get_status() ); } + public function test_create_comment_no_post_id_no_permission() { + wp_set_current_user( $this->subscriber_id ); + + $params = array( + 'author_name' => 'Homer Jay Simpson', + 'author_email' => 'chunkylover53@aol.com', + 'author_url' => 'http://compuglobalhypermeganet.com', + 'content' => 'Here\’s to alcohol: the cause of, and solution to, all of life\’s problems.', + 'author' => $this->subscriber_id, + ); + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_comment_invalid_post_id', $response, 403 ); + } + + public function test_create_comment_private_post_invalide_permission() { + wp_set_current_user( $this->subscriber_id ); + + $params = array( + 'post' => $this->private_id, + 'author_name' => 'Homer Jay Simpson', + 'author_email' => 'chunkylover53@aol.com', + 'author_url' => 'http://compuglobalhypermeganet.com', + 'content' => 'I\’d be a vegetarian if bacon grew on trees.', + 'author' => $this->subscriber_id, + ); + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_cannot_read_post', $response, 403 ); + } + public function test_create_item_duplicate() { $this->markTestSkipped( 'Needs to be revisited after wp_die handling is added' ); $this->factory->comment->create( @@ -1191,6 +1321,26 @@ public function test_update_comment_invalid_permission() { $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); } + public function test_update_comment_private_post_invalid_permission() { + $private_comment_id = $this->factory->comment->create( array( + 'comment_approved' => 1, + 'comment_post_ID' => $this->private_id, + 'user_id' => 0, + )); + + wp_set_current_user( $this->subscriber_id ); + + $params = array( + 'content' => 'Disco Stu likes disco music.', + ); + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $private_comment_id ) ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 ); + } + public function test_update_comment_with_children_link() { wp_set_current_user( $this->admin_id ); $comment_id_1 = $this->factory->comment->create( array( From 9bf03a0fd2514995d8727f2ce04a2bc7d1de4192 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 30 Sep 2016 18:26:13 -0400 Subject: [PATCH 155/318] Update CHANGELOG for 2.0 Beta 14 --- CHANGELOG.md | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7acee4a019..fd6abf679f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,245 @@ # Changelog +## 2.0 Beta 14.0 (September 30, 2016) + +- Password Protected Posts Support + + (props @joehoyle, [#2720][gh-2720]) + +- Allow returning an error from field update callbacks + + (props @rmccue, [#2702][gh-2702]) + +- Add "relevance" orderby to posts endpoint + + (props @websupporter, [#2579][gh-2579]) + +- Ability to orderby slug, email and url on users endpoints. + + (props @joehoyle, [#2721][gh-2721]) + +- Add sticky parameter to the posts endpoint. + + (props @joehoyle, [#2708][gh-2708]) + +- #1612 Add link to comment children, allowing threaded comment querying + + (props @BE-Webdesign, [#2662][gh-2662], [#1612][gh-1612]) + +- Avoid unnecessary SQL query by passing `$user_nicename` + + (props @danielbachhuber, [#2435][gh-2435]) + +- Don't allow reading / creating of posts with no parent + + (props @rachelbaker, [#2744][gh-2744]) + +- Mark Users' `capabilities` property as readonly + + (props @danielbachhuber, [#2440][gh-2440]) + +- Mark some post properties as readonly + + (props @danielbachhuber, [#2438][gh-2438], [#2439][gh-2439]) + +- Use WPINC instead of wp-includes/ + + (props @websupporter, [#2461][gh-2461]) + +- Return error, if user can't list users & content=edit + + (props @websupporter, [#2463][gh-2463]) + +- Conditionally model the term response based on its schema + + (props @danielbachhuber, [#2470][gh-2470]) + +- Include Post data on the response object when declared + + (props @websupporter, [#2423][gh-2423], [#2416][gh-2416]) + +- Add boolean type support to rest_validate_request_arg() + + (props @westonruter, [#2478][gh-2478]) + +- Fix create/update requests not processing data included in the schema + + (props @websupporter, [#2479][gh-2479]) + +- Remove Unused Parameter in lib/endpoints/class-wp-rest-controller.php + + (props @hideokamoto, [#2500][gh-2500]) + +- Update post schema status description to reflect csv support. + + (props @coderkevin, [#2534][gh-2534]) + +- Allow Comments to be created with a passed `author_ip` + + (props @rachelbaker, [#1880][gh-1880]) + +- The get_the_excerpt filter expects the post object as of WP 4.5. + + (props @lgedeon, [#2553][gh-2553]) + +- Introduce WP_REST_Controller::get_post() for allowing plugins to mutate +get_post()'s return value + + (props @westonruter, [#2535][gh-2535]) + +- Use `show_in_rest` to determine "public" post types to check + + (props @danielbachhuber, [#2384][gh-2384]) + +- #2426 Fix inconsistent type for user caps + + (props @BE-Webdesign, [#2429][gh-2429], [#2426][gh-2426]) + +- Define user `type` as a string, not an array + + (props @danielbachhuber, [#2556][gh-2556]) + +- Fix failing test: Typecast the user_id in search to a string + + (props @rachelbaker, [#2617][gh-2617]) + +- #2587 Fix registered date schema + + (props @BE-Webdesign, [#2628][gh-2628], [#2587][gh-2587]) + +- Fix forum url and installer-name in readme + + (props @torounit, [#2656][gh-2656]) + +- Document options of the "status" parameter for Post collection GETs + + (props @kadamwhite, [#2645][gh-2645]) + +- Improve WP_REST_Controller::filter_response_by_context(). + + (props @tfrommen, [#2641][gh-2641]) + +- #2424 Consistent slashes in rest_url() usage + + (props @BE-Webdesign, [#2428][gh-2428], [#2424][gh-2424]) + +- Add filters to allow for relevance search + + (props @websupporter, [#2665][gh-2665]) + +- Alter default comment sort order to be "desc" + + (props @kadamwhite, [#2684][gh-2684]) + +- Add raw and rendered to revisions schema + + (props @websupporter, [#2693][gh-2693]) + +- "WP API" -> "WordPress REST API" in README files + + (props @kadamwhite, [#2697][gh-2697]) + +- Improve boolean validation from schema + + (props @BE-Webdesign, [#2704][gh-2704], [#2616][gh-2616]) + +- Fix typo (PUT vs POST) in readme.md + + (props @kadamwhite, [#2716][gh-2716]) + +- Add Codecov configuration + + (props @danielbachhuber, [#2718][gh-2718]) + +- Fix inefficiency in users endpoint using `search => **` + + (props @joehoyle, [#2722][gh-2722]) + +- Ensure the terms list is a list + + (props @joehoyle, [#2724][gh-2724]) + +- Added @return on handle_featured_media() doc + + (props @vishalkakadiya, [#2725][gh-2725]) + +- #2730 Update attachments fields added with `register_rest_field` + + (props @BE-Webdesign, [#2731][gh-2731], [#2730][gh-2730]) + +- #2582 Ensure the `roles` property is always an array + + (props @BE-Webdesign, [#2728][gh-2728], [#2582][gh-2582]) + +- Move post_password_required filtering to preparation + + (props @rmccue, [#2735][gh-2735]) + +- Use wrapper for `sanitize_title` to avoid messed up slugs. + + (props @joehoyle, [#2723][gh-2723]) + +- Force per_page to override the filter variable + + (props @rmccue, [#2699][gh-2699]) + +[gh-1612]: https://github.com/WP-API/WP-API/issues/1612 +[gh-1880]: https://github.com/WP-API/WP-API/issues/1880 +[gh-2384]: https://github.com/WP-API/WP-API/issues/2384 +[gh-2416]: https://github.com/WP-API/WP-API/issues/2416 +[gh-2423]: https://github.com/WP-API/WP-API/issues/2423 +[gh-2424]: https://github.com/WP-API/WP-API/issues/2424 +[gh-2426]: https://github.com/WP-API/WP-API/issues/2426 +[gh-2428]: https://github.com/WP-API/WP-API/issues/2428 +[gh-2429]: https://github.com/WP-API/WP-API/issues/2429 +[gh-2435]: https://github.com/WP-API/WP-API/issues/2435 +[gh-2436]: https://github.com/WP-API/WP-API/issues/2436 +[gh-2438]: https://github.com/WP-API/WP-API/issues/2438 +[gh-2439]: https://github.com/WP-API/WP-API/issues/2439 +[gh-2440]: https://github.com/WP-API/WP-API/issues/2440 +[gh-2441]: https://github.com/WP-API/WP-API/issues/2441 +[gh-2461]: https://github.com/WP-API/WP-API/issues/2461 +[gh-2463]: https://github.com/WP-API/WP-API/issues/2463 +[gh-2470]: https://github.com/WP-API/WP-API/issues/2470 +[gh-2478]: https://github.com/WP-API/WP-API/issues/2478 +[gh-2479]: https://github.com/WP-API/WP-API/issues/2479 +[gh-2500]: https://github.com/WP-API/WP-API/issues/2500 +[gh-2534]: https://github.com/WP-API/WP-API/issues/2534 +[gh-2535]: https://github.com/WP-API/WP-API/issues/2535 +[gh-2553]: https://github.com/WP-API/WP-API/issues/2553 +[gh-2556]: https://github.com/WP-API/WP-API/issues/2556 +[gh-2579]: https://github.com/WP-API/WP-API/issues/2579 +[gh-2582]: https://github.com/WP-API/WP-API/issues/2582 +[gh-2587]: https://github.com/WP-API/WP-API/issues/2587 +[gh-2616]: https://github.com/WP-API/WP-API/issues/2616 +[gh-2617]: https://github.com/WP-API/WP-API/issues/2617 +[gh-2628]: https://github.com/WP-API/WP-API/issues/2628 +[gh-2641]: https://github.com/WP-API/WP-API/issues/2641 +[gh-2645]: https://github.com/WP-API/WP-API/issues/2645 +[gh-2656]: https://github.com/WP-API/WP-API/issues/2656 +[gh-2662]: https://github.com/WP-API/WP-API/issues/2662 +[gh-2665]: https://github.com/WP-API/WP-API/issues/2665 +[gh-2684]: https://github.com/WP-API/WP-API/issues/2684 +[gh-2693]: https://github.com/WP-API/WP-API/issues/2693 +[gh-2697]: https://github.com/WP-API/WP-API/issues/2697 +[gh-2699]: https://github.com/WP-API/WP-API/issues/2699 +[gh-2702]: https://github.com/WP-API/WP-API/issues/2702 +[gh-2704]: https://github.com/WP-API/WP-API/issues/2704 +[gh-2708]: https://github.com/WP-API/WP-API/issues/2708 +[gh-2716]: https://github.com/WP-API/WP-API/issues/2716 +[gh-2718]: https://github.com/WP-API/WP-API/issues/2718 +[gh-2720]: https://github.com/WP-API/WP-API/issues/2720 +[gh-2721]: https://github.com/WP-API/WP-API/issues/2721 +[gh-2722]: https://github.com/WP-API/WP-API/issues/2722 +[gh-2723]: https://github.com/WP-API/WP-API/issues/2723 +[gh-2724]: https://github.com/WP-API/WP-API/issues/2724 +[gh-2725]: https://github.com/WP-API/WP-API/issues/2725 +[gh-2728]: https://github.com/WP-API/WP-API/issues/2728 +[gh-2730]: https://github.com/WP-API/WP-API/issues/2730 +[gh-2731]: https://github.com/WP-API/WP-API/issues/2731 +[gh-2735]: https://github.com/WP-API/WP-API/issues/2735 +[gh-2744]: https://github.com/WP-API/WP-API/issues/2744 + ## 2.0 Beta 13.0 (March 29, 2016) - BREAKING CHANGE: Fix Content-Disposition header parsing. From e7ec734dda7c589c18ba1dd6c0600288e7202c53 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 30 Sep 2016 18:48:47 -0400 Subject: [PATCH 156/318] Sync Client JS Bring over the latest version of the client js library from https://github.com/WP-API/client-js --- wp-api.js | 108 +++++++++++++++++++++++++++++-------------------- wp-api.min.js | 2 +- wp-api.min.map | 2 +- 3 files changed, 67 insertions(+), 45 deletions(-) diff --git a/wp-api.js b/wp-api.js index 2ebb97cc97..d1fadcf5b0 100755 --- a/wp-api.js +++ b/wp-api.js @@ -2,6 +2,9 @@ 'use strict'; + /** + * Initialise the WP_API. + */ function WP_API() { this.models = {}; this.collections = {}; @@ -12,7 +15,7 @@ wp.api = wp.api || new WP_API(); wp.api.versionString = wp.api.versionString || 'wp/v2/'; - // Alias _includes to _.contains, ensuring it is available. + // Alias _includes to _.contains, ensuring it is available if lodash is used. if ( ! _.isFunction( _.includes ) && _.isFunction( _.contains ) ) { _.includes = _.contains; } @@ -117,7 +120,7 @@ }; /** - * Extract a route part based on negitive index. + * Extract a route part based on negative index. * * @param {string} route The endpoint route. * @param {int} part The number of parts from the end of the route to retrieve. Default 1. @@ -250,8 +253,8 @@ _.each( parseableDates, function( key ) { if ( key in attributes ) { - // Don't convert null values - if ( ! _.isNull( attributes[ key ] ) ) { + // Only convert dates. + if ( _.isDate( attributes[ key ] ) ) { attributes[ key ] = attributes[ key ].toISOString(); } } @@ -275,7 +278,7 @@ return; } - // Don't convert null values + // Don't convert null values. if ( ! _.isNull( response[ key ] ) ) { timestamp = wp.api.utils.parseISO8601( response[ key ] ); response[ key ] = new Date( timestamp ); @@ -303,8 +306,8 @@ deferred = jQuery.Deferred(); embeddeds = parentModel.get( '_embedded' ) || {}; - // Verify that we have a valied author id. - if ( ! _.isNumber( modelId ) ) { + // Verify that we have a valied object id. + if ( ! _.isNumber( modelId ) || 0 === modelId ) { deferred.reject(); return deferred; } @@ -619,11 +622,11 @@ }, /** - * Add a helper function to retrieve the featured image. + * Add a helper function to retrieve the featured media. */ - FeaturedImageMixin = { - getFeaturedImage: function() { - return buildModelGetter( this, this.get( 'featured_image' ), 'Media', 'https://api.w.org/featuredmedia', 'source_url' ); + FeaturedMediaMixin = { + getFeaturedMedia: function() { + return buildModelGetter( this, this.get( 'featured_media' ), 'Media', 'wp:featuredmedia', 'source_url' ); } }; @@ -649,9 +652,9 @@ model = model.extend( AuthorMixin ); } - // Add the FeaturedImageMixin for models that contain a featured_image. - if ( ! _.isUndefined( model.prototype.args.featured_image ) ) { - model = model.extend( FeaturedImageMixin ); + // Add the FeaturedMediaMixin for models that contain a featured_media. + if ( ! _.isUndefined( model.prototype.args.featured_media ) ) { + model = model.extend( FeaturedMediaMixin ); } // Add the CategoriesMixin for models that support categories collections. @@ -837,7 +840,7 @@ }, /** - * Overwrite Backbone.Collection.sync to pagination state based on response headers. + * Extend Backbone.Collection.sync to add nince and pagination support. * * Set nonce header before every Backbone sync. * @@ -853,6 +856,7 @@ options = options || {}; beforeSend = options.beforeSend; + // If we have a localized nonce, pass that along with each sync. if ( 'undefined' !== typeof wpApiSettings.nonce ) { options.beforeSend = function( xhr ) { xhr.setRequestHeader( 'X-WP-Nonce', wpApiSettings.nonce ); @@ -863,6 +867,7 @@ }; } + // When reading, add pagination data. if ( 'read' === method ) { if ( options.data ) { self.state.data = _.clone( options.data ); @@ -873,8 +878,8 @@ } if ( 'undefined' === typeof options.data.page ) { - self.state.currentPage = null; - self.state.totalPages = null; + self.state.currentPage = null; + self.state.totalPages = null; self.state.totalObjects = null; } else { self.state.currentPage = options.data.page - 1; @@ -883,7 +888,7 @@ success = options.success; options.success = function( data, textStatus, request ) { if ( ! _.isUndefined( request ) ) { - self.state.totalPages = parseInt( request.getResponseHeader( 'x-wp-totalpages' ), 10 ); + self.state.totalPages = parseInt( request.getResponseHeader( 'x-wp-totalpages' ), 10 ); self.state.totalObjects = parseInt( request.getResponseHeader( 'x-wp-total' ), 10 ); } @@ -899,6 +904,7 @@ }; } + // Continue by calling Bacckbone's sync. return Backbone.sync( method, model, options ); }, @@ -957,11 +963,12 @@ window.wp = window.wp || {}; wp.api = wp.api || {}; + // If wpApiSettings is unavailable, try the default. if ( _.isEmpty( wpApiSettings ) ) { wpApiSettings.root = window.location.origin + '/wp-json/'; } - Endpoint = Backbone.Model.extend({ + Endpoint = Backbone.Model.extend( { defaults: { apiRoot: wpApiSettings.root, versionString: wp.api.versionString, @@ -970,6 +977,9 @@ collections: {} }, + /** + * Initialize the Endpoint model. + */ initialize: function() { var model = this, deferred; @@ -981,8 +991,9 @@ model.schemaModel = new wp.api.models.Schema( null, { apiRoot: model.get( 'apiRoot' ), versionString: model.get( 'versionString' ) - }); + } ); + // When the model loads, resolve the promise. model.schemaModel.once( 'change', function() { model.constructFromSchema(); deferred.resolve( model ); @@ -992,29 +1003,39 @@ // Use schema supplied as model attribute. model.schemaModel.set( model.schemaModel.parse( model.get( 'schema' ) ) ); - } else if ( ! _.isUndefined( sessionStorage ) && sessionStorage.getItem( 'wp-api-schema-model' + model.get( 'apiRoot' ) + model.get( 'versionString' ) ) ) { + } else if ( + ! _.isUndefined( sessionStorage ) && + ( _.isUndefined( wpApiSettings.cacheSchema ) || wpApiSettings.cacheSchema ) && + sessionStorage.getItem( 'wp-api-schema-model' + model.get( 'apiRoot' ) + model.get( 'versionString' ) ) + ) { // Used a cached copy of the schema model if available. model.schemaModel.set( model.schemaModel.parse( JSON.parse( sessionStorage.getItem( 'wp-api-schema-model' + model.get( 'apiRoot' ) + model.get( 'versionString' ) ) ) ) ); } else { - model.schemaModel.fetch({ + model.schemaModel.fetch( { /** - * When the server return the schema model data, store the data in a sessionCache so we don't + * When the server returns the schema model data, store the data in a sessionCache so we don't * have to retrieve it again for this session. Then, construct the models and collections based * on the schema model data. */ success: function( newSchemaModel ) { // Store a copy of the schema model in the session cache if available. - if ( ! _.isUndefined( sessionStorage ) ) { - sessionStorage.setItem( 'wp-api-schema-model' + model.get( 'apiRoot' ) + model.get( 'versionString' ), JSON.stringify( newSchemaModel ) ); + if ( ! _.isUndefined( sessionStorage ) && wpApiSettings.cacheSchema ) { + try { + sessionStorage.setItem( 'wp-api-schema-model' + model.get( 'apiRoot' ) + model.get( 'versionString' ), JSON.stringify( newSchemaModel ) ); + } catch ( error ) { + + // Fail silently, fixes errors in safari private mode. + } } }, - // @todo Handle the error condition. - error: function() { + // Log the error condition. + error: function( err ) { + window.console.log( err ); } - }); + } ); } }, @@ -1060,10 +1081,10 @@ * Iterate thru the routes, picking up models and collections to build. Builds two arrays, * one for models and one for collections. */ - modelRoutes = []; - collectionRoutes = []; - schemaRoot = routeModel.get( 'apiRoot' ).replace( wp.api.utils.getRootUrl(), '' ); - loadingObjects = {}; + modelRoutes = []; + collectionRoutes = []; + schemaRoot = routeModel.get( 'apiRoot' ).replace( wp.api.utils.getRootUrl(), '' ); + loadingObjects = {}; /** * Tracking objects for models and collections. @@ -1080,7 +1101,7 @@ ) { // Single items end with a regex (or the special case 'me'). - if ( /.*[+)|me]$/.test( index ) ) { + if ( /(?:.*[+)]|\/me)$/.test( index ) ) { modelRoutes.push( { index: index, route: route } ); } else { @@ -1114,7 +1135,7 @@ modelClassName = mapping.models[ modelClassName ] || modelClassName; loadingObjects.models[ modelClassName ] = wp.api.WPApiBaseModel.extend( { - // Function that returns a constructed url based on the parent and id. + // Return a constructed url based on the parent and id. url: function() { var url = routeModel.get( 'apiRoot' ) + routeModel.get( 'versionString' ) + parentName + '/' + @@ -1138,6 +1159,7 @@ methods: modelRoute.route.methods, initialize: function() { + /** * Posts and pages support trashing, other types don't support a trash * and require that you pass ?force=true to actually delete them. @@ -1183,7 +1205,7 @@ } ); } - // Add defaults to the new model, pulled form the endpoint + // Add defaults to the new model, pulled form the endpoint. wp.api.utils.decorateFromRoute( modelRoute.route.endpoints, loadingObjects.models[ modelClassName ] ); } ); @@ -1200,7 +1222,7 @@ routeName = collectionRoute.index.slice( collectionRoute.index.lastIndexOf( '/' ) + 1 ), parentName = wp.api.utils.extractRoutePart( collectionRoute.index, 3 ); - // If the collection has a parent in its route, add that to its class name/ + // If the collection has a parent in its route, add that to its class name. if ( '' !== parentName && parentName !== routeName ) { collectionClassName = wp.api.utils.capitalize( parentName ) + wp.api.utils.capitalize( routeName ); @@ -1252,7 +1274,7 @@ } ); } - // Add defaults to the new model, pulled form the endpoint + // Add defaults to the new model, pulled form the endpoint. wp.api.utils.decorateFromRoute( collectionRoute.route.endpoints, loadingObjects.collections[ collectionClassName ] ); } ); @@ -1263,11 +1285,11 @@ } - }); + } ); - wp.api.endpoints = new Backbone.Collection({ + wp.api.endpoints = new Backbone.Collection( { model: Endpoint - }); + } ); /** * Initialize the wp-api, optionally passing the API root. @@ -1280,10 +1302,10 @@ wp.api.init = function( args ) { var endpoint, attributes = {}, deferred, promise; - args = args || {}; - attributes.apiRoot = args.apiRoot || wpApiSettings.root; + args = args || {}; + attributes.apiRoot = args.apiRoot || wpApiSettings.root; attributes.versionString = args.versionString || wpApiSettings.versionString; - attributes.schema = args.schema || null; + attributes.schema = args.schema || null; if ( ! attributes.schema && attributes.apiRoot === wpApiSettings.root && attributes.versionString === wpApiSettings.versionString ) { attributes.schema = wpApiSettings.schema; } diff --git a/wp-api.min.js b/wp-api.min.js index de28e22095..b6c13c9299 100644 --- a/wp-api.min.js +++ b/wp-api.min.js @@ -1,2 +1,2 @@ -!function(a,b){"use strict";function c(){this.models={},this.collections={},this.views={}}a.wp=a.wp||{},wp.api=wp.api||new c,wp.api.versionString=wp.api.versionString||"wp/v2/",!_.isFunction(_.includes)&&_.isFunction(_.contains)&&(_.includes=_.contains)}(window),function(a,b){"use strict";var c,d;a.wp=a.wp||{},wp.api=wp.api||{},wp.api.utils=wp.api.utils||{},Date.prototype.toISOString||(c=function(a){return d=String(a),1===d.length&&(d="0"+d),d},Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+c(this.getUTCMonth()+1)+"-"+c(this.getUTCDate())+"T"+c(this.getUTCHours())+":"+c(this.getUTCMinutes())+":"+c(this.getUTCSeconds())+"."+String((this.getUTCMilliseconds()/1e3).toFixed(3)).slice(2,5)+"Z"}),wp.api.utils.parseISO8601=function(a){var c,d,e,f,g=0,h=[1,4,5,6,7,10,11];if(d=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(a)){for(e=0;f=h[e];++e)d[f]=+d[f]||0;d[2]=(+d[2]||1)-1,d[3]=+d[3]||1,"Z"!==d[8]&&b!==d[9]&&(g=60*d[10]+d[11],"+"===d[9]&&(g=0-g)),c=Date.UTC(d[1],d[2],d[3],d[4],d[5]+g,d[6],d[7])}else c=Date.parse?Date.parse(a):NaN;return c},wp.api.utils.getRootUrl=function(){return a.location.origin?a.location.origin+"/":a.location.protocol+"/"+a.location.host+"/"},wp.api.utils.capitalize=function(a){return _.isUndefined(a)?a:a.charAt(0).toUpperCase()+a.slice(1)},wp.api.utils.extractRoutePart=function(a,b){var c;return b=b||1,a=a.replace(wp.api.versionString,""),c=a.split("/").reverse(),_.isUndefined(c[--b])?"":c[b]},wp.api.utils.extractParentName=function(a){var b,c=a.lastIndexOf("_id>[\\d]+)/");return 0>c?"":(b=a.substr(0,c-1),b=b.split("/"),b.pop(),b=b.pop())},wp.api.utils.decorateFromRoute=function(a,b){_.each(a,function(a){_.includes(a.methods,"POST")||_.includes(a.methods,"PUT")?_.isEmpty(a.args)||(_.isEmpty(b.prototype.args)?b.prototype.args=a.args:b.prototype.args=_.union(a.args,b.prototype.defaults)):_.includes(a.methods,"GET")&&(_.isEmpty(a.args)||(_.isEmpty(b.prototype.options)?b.prototype.options=a.args:b.prototype.options=_.union(a.args,b.prototype.options)))})},wp.api.utils.addMixinsAndHelpers=function(a,b,c){var d=!1,e=["date","modified","date_gmt","modified_gmt"],f={toJSON:function(){var a=_.clone(this.attributes);return _.each(e,function(b){b in a&&(_.isNull(a[b])||(a[b]=a[b].toISOString()))}),a},parse:function(a){var b;return _.each(e,function(c){c in a&&(_.isNull(a[c])||(b=wp.api.utils.parseISO8601(a[c]),a[c]=new Date(b)))}),a}},g=function(a,b,c,d,e){var f,g,h,i;return i=jQuery.Deferred(),g=a.get("_embedded")||{},_.isNumber(b)?(g[d]&&(h=_.findWhere(g[d],{id:b})),h||(h={id:b}),f=new wp.api.models[c](h),f.get(e)?i.resolve(f):f.fetch({success:function(a){i.resolve(a)}}),i.promise()):(i.reject(),i)},h=function(a,b,c,d){var e,f,g,h="",j="",k=jQuery.Deferred();return e=a.get("id"),f=a.get("_embedded")||{},_.isNumber(e)&&0!==e?(_.isUndefined(c)||_.isUndefined(f[c])?h={parent:e}:j=_.isUndefined(d)?f[c]:f[c][d],g=new wp.api.collections[b](j,h),_.isUndefined(g.models[0])?g.fetch({success:function(a){i(a,e),k.resolve(a)}}):(i(g,e),k.resolve(g)),k.promise()):(k.reject(),k)},i=function(a,b){_.each(a.models,function(a){a.set("parent_post",b)})},j={getMeta:function(){return h(this,"PostMeta","https://api.w.org/meta")}},k={getRevisions:function(){return h(this,"PostRevisions")}},l={getTags:function(){var a=this.get("tags"),b=new wp.api.collections.Tags;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setTags:function(a){var b,c,d=this,e=[];return _.isString(a)?!1:void(_.isArray(a)?(b=new wp.api.collections.Tags,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Tag(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Tags(e),d.setTagsWithCollection(a)}})):this.setTagsWithCollection(a))},setTagsWithCollection:function(a){return this.set("tags",a.pluck("id")),this.save()}},m={getCategories:function(){var a=this.get("categories"),b=new wp.api.collections.Categories;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setCategories:function(a){var b,c,d=this,e=[];return _.isString(a)?!1:void(_.isArray(a)?(b=new wp.api.collections.Categories,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Category(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Categories(e),d.setCategoriesWithCollection(a)}})):this.setCategoriesWithCollection(a))},setCategoriesWithCollection:function(a){return this.set("categories",a.pluck("id")),this.save()}},n={getAuthorUser:function(){return g(this,this.get("author"),"User","author","name")}},o={getFeaturedImage:function(){return g(this,this.get("featured_image"),"Media","https://api.w.org/featuredmedia","source_url")}};return _.isUndefined(a.prototype.args)?a:(_.each(e,function(b){_.isUndefined(a.prototype.args[b])||(d=!0)}),d&&(a=a.extend(f)),_.isUndefined(a.prototype.args.author)||(a=a.extend(n)),_.isUndefined(a.prototype.args.featured_image)||(a=a.extend(o)),_.isUndefined(a.prototype.args.categories)||(a=a.extend(m)),_.isUndefined(c.collections[b+"Meta"])||(a=a.extend(j)),_.isUndefined(a.prototype.args.tags)||(a=a.extend(l)),_.isUndefined(c.collections[b+"Revisions"])||(a=a.extend(k)),a)}}(window),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseModel=Backbone.Model.extend({sync:function(b,c,d){var e;return d=d||{},_.isNull(c.get("date_gmt"))&&c.unset("date_gmt"),_.isEmpty(c.get("slug"))&&c.unset("slug"),_.isUndefined(a.nonce)||_.isNull(a.nonce)||(e=d.beforeSend,d.beforeSend=function(b){return b.setRequestHeader("X-WP-Nonce",a.nonce),e?e.apply(this,arguments):void 0}),this.requireForceForDelete&&"delete"===b&&(c.url=c.url()+"?force=true"),Backbone.sync(b,c,d)},save:function(a,b){return _.includes(this.methods,"PUT")||_.includes(this.methods,"POST")?Backbone.Model.prototype.save.call(this,a,b):!1},destroy:function(a){return _.includes(this.methods,"DELETE")?Backbone.Model.prototype.destroy.call(this,a):!1}}),wp.api.models.Schema=wp.api.WPApiBaseModel.extend({defaults:{_links:{},namespace:null,routes:{}},initialize:function(b,c){var d=this;c=c||{},wp.api.WPApiBaseModel.prototype.initialize.call(d,b,c),d.apiRoot=c.apiRoot||a.root,d.versionString=c.versionString||a.versionString},url:function(){return this.apiRoot+this.versionString}})}(),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseCollection=Backbone.Collection.extend({initialize:function(a,b){this.state={data:{},currentPage:null,totalPages:null,totalObjects:null},_.isUndefined(b)?this.parent="":this.parent=b.parent},sync:function(b,c,d){var e,f,g=this;return d=d||{},e=d.beforeSend,"undefined"!=typeof a.nonce&&(d.beforeSend=function(b){return b.setRequestHeader("X-WP-Nonce",a.nonce),e?e.apply(g,arguments):void 0}),"read"===b&&(d.data?(g.state.data=_.clone(d.data),delete g.state.data.page):g.state.data=d.data={},"undefined"==typeof d.data.page?(g.state.currentPage=null,g.state.totalPages=null,g.state.totalObjects=null):g.state.currentPage=d.data.page-1,f=d.success,d.success=function(a,b,c){return _.isUndefined(c)||(g.state.totalPages=parseInt(c.getResponseHeader("x-wp-totalpages"),10),g.state.totalObjects=parseInt(c.getResponseHeader("x-wp-total"),10)),null===g.state.currentPage?g.state.currentPage=1:g.state.currentPage++,f?f.apply(this,arguments):void 0}),Backbone.sync(b,c,d)},more:function(a){if(a=a||{},a.data=a.data||{},_.extend(a.data,this.state.data),"undefined"==typeof a.data.page){if(!this.hasMore())return!1;null===this.state.currentPage||this.state.currentPage<=1?a.data.page=2:a.data.page=this.state.currentPage+1}return this.fetch(a)},hasMore:function(){return null===this.state.totalPages||null===this.state.totalObjects||null===this.state.currentPage?null:this.state.currentPage[\\d]+)/");return c<0?"":(b=a.substr(0,c-1),b=b.split("/"),b.pop(),b=b.pop())},wp.api.utils.decorateFromRoute=function(a,b){_.each(a,function(a){_.includes(a.methods,"POST")||_.includes(a.methods,"PUT")?_.isEmpty(a.args)||(_.isEmpty(b.prototype.args)?b.prototype.args=a.args:b.prototype.args=_.union(a.args,b.prototype.defaults)):_.includes(a.methods,"GET")&&(_.isEmpty(a.args)||(_.isEmpty(b.prototype.options)?b.prototype.options=a.args:b.prototype.options=_.union(a.args,b.prototype.options)))})},wp.api.utils.addMixinsAndHelpers=function(a,b,c){var d=!1,e=["date","modified","date_gmt","modified_gmt"],f={toJSON:function(){var a=_.clone(this.attributes);return _.each(e,function(b){b in a&&_.isDate(a[b])&&(a[b]=a[b].toISOString())}),a},parse:function(a){var b;return _.each(e,function(c){c in a&&(_.isNull(a[c])||(b=wp.api.utils.parseISO8601(a[c]),a[c]=new Date(b)))}),a}},g=function(a,b,c,d,e){var f,g,h,i;return i=jQuery.Deferred(),g=a.get("_embedded")||{},_.isNumber(b)&&0!==b?(g[d]&&(h=_.findWhere(g[d],{id:b})),h||(h={id:b}),f=new wp.api.models[c](h),f.get(e)?i.resolve(f):f.fetch({success:function(a){i.resolve(a)}}),i.promise()):(i.reject(),i)},h=function(a,b,c,d){var e,f,g,h="",j="",k=jQuery.Deferred();return e=a.get("id"),f=a.get("_embedded")||{},_.isNumber(e)&&0!==e?(_.isUndefined(c)||_.isUndefined(f[c])?h={parent:e}:j=_.isUndefined(d)?f[c]:f[c][d],g=new wp.api.collections[b](j,h),_.isUndefined(g.models[0])?g.fetch({success:function(a){i(a,e),k.resolve(a)}}):(i(g,e),k.resolve(g)),k.promise()):(k.reject(),k)},i=function(a,b){_.each(a.models,function(a){a.set("parent_post",b)})},j={getMeta:function(){return h(this,"PostMeta","https://api.w.org/meta")}},k={getRevisions:function(){return h(this,"PostRevisions")}},l={getTags:function(){var a=this.get("tags"),b=new wp.api.collections.Tags;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setTags:function(a){var b,c,d=this,e=[];return!_.isString(a)&&void(_.isArray(a)?(b=new wp.api.collections.Tags,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Tag(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Tags(e),d.setTagsWithCollection(a)}})):this.setTagsWithCollection(a))},setTagsWithCollection:function(a){return this.set("tags",a.pluck("id")),this.save()}},m={getCategories:function(){var a=this.get("categories"),b=new wp.api.collections.Categories;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setCategories:function(a){var b,c,d=this,e=[];return!_.isString(a)&&void(_.isArray(a)?(b=new wp.api.collections.Categories,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Category(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Categories(e),d.setCategoriesWithCollection(a)}})):this.setCategoriesWithCollection(a))},setCategoriesWithCollection:function(a){return this.set("categories",a.pluck("id")),this.save()}},n={getAuthorUser:function(){return g(this,this.get("author"),"User","author","name")}},o={getFeaturedMedia:function(){return g(this,this.get("featured_media"),"Media","wp:featuredmedia","source_url")}};return _.isUndefined(a.prototype.args)?a:(_.each(e,function(b){_.isUndefined(a.prototype.args[b])||(d=!0)}),d&&(a=a.extend(f)),_.isUndefined(a.prototype.args.author)||(a=a.extend(n)),_.isUndefined(a.prototype.args.featured_media)||(a=a.extend(o)),_.isUndefined(a.prototype.args.categories)||(a=a.extend(m)),_.isUndefined(c.collections[b+"Meta"])||(a=a.extend(j)),_.isUndefined(a.prototype.args.tags)||(a=a.extend(l)),_.isUndefined(c.collections[b+"Revisions"])||(a=a.extend(k)),a)}}(window),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseModel=Backbone.Model.extend({sync:function(b,c,d){var e;return d=d||{},_.isNull(c.get("date_gmt"))&&c.unset("date_gmt"),_.isEmpty(c.get("slug"))&&c.unset("slug"),_.isUndefined(a.nonce)||_.isNull(a.nonce)||(e=d.beforeSend,d.beforeSend=function(b){if(b.setRequestHeader("X-WP-Nonce",a.nonce),e)return e.apply(this,arguments)}),this.requireForceForDelete&&"delete"===b&&(c.url=c.url()+"?force=true"),Backbone.sync(b,c,d)},save:function(a,b){return!(!_.includes(this.methods,"PUT")&&!_.includes(this.methods,"POST"))&&Backbone.Model.prototype.save.call(this,a,b)},destroy:function(a){return!!_.includes(this.methods,"DELETE")&&Backbone.Model.prototype.destroy.call(this,a)}}),wp.api.models.Schema=wp.api.WPApiBaseModel.extend({defaults:{_links:{},namespace:null,routes:{}},initialize:function(b,c){var d=this;c=c||{},wp.api.WPApiBaseModel.prototype.initialize.call(d,b,c),d.apiRoot=c.apiRoot||a.root,d.versionString=c.versionString||a.versionString},url:function(){return this.apiRoot+this.versionString}})}(),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseCollection=Backbone.Collection.extend({initialize:function(a,b){this.state={data:{},currentPage:null,totalPages:null,totalObjects:null},_.isUndefined(b)?this.parent="":this.parent=b.parent},sync:function(b,c,d){var e,f,g=this;return d=d||{},e=d.beforeSend,"undefined"!=typeof a.nonce&&(d.beforeSend=function(b){if(b.setRequestHeader("X-WP-Nonce",a.nonce),e)return e.apply(g,arguments)}),"read"===b&&(d.data?(g.state.data=_.clone(d.data),delete g.state.data.page):g.state.data=d.data={},"undefined"==typeof d.data.page?(g.state.currentPage=null,g.state.totalPages=null,g.state.totalObjects=null):g.state.currentPage=d.data.page-1,f=d.success,d.success=function(a,b,c){if(_.isUndefined(c)||(g.state.totalPages=parseInt(c.getResponseHeader("x-wp-totalpages"),10),g.state.totalObjects=parseInt(c.getResponseHeader("x-wp-total"),10)),null===g.state.currentPage?g.state.currentPage=1:g.state.currentPage++,f)return f.apply(this,arguments)}),Backbone.sync(b,c,d)},more:function(a){if(a=a||{},a.data=a.data||{},_.extend(a.data,this.state.data),"undefined"==typeof a.data.page){if(!this.hasMore())return!1;null===this.state.currentPage||this.state.currentPage<=1?a.data.page=2:a.data.page=this.state.currentPage+1}return this.fetch(a)},hasMore:function(){return null===this.state.totalPages||null===this.state.totalObjects||null===this.state.currentPage?null:this.state.currentPage Date: Fri, 30 Sep 2016 18:53:45 -0400 Subject: [PATCH 157/318] Add note on JS client --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd6abf679f..fcbc48ff17 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ (props @BE-Webdesign, [#2662][gh-2662], [#1612][gh-1612]) +- Update the wp-api.js client from the client-js repo. + + (props @joehoyle, [#2746][gh-2746]) + - Avoid unnecessary SQL query by passing `$user_nicename` (props @danielbachhuber, [#2435][gh-2435]) @@ -239,6 +243,7 @@ get_post()'s return value [gh-2731]: https://github.com/WP-API/WP-API/issues/2731 [gh-2735]: https://github.com/WP-API/WP-API/issues/2735 [gh-2744]: https://github.com/WP-API/WP-API/issues/2744 +[gh-2746]: https://github.com/WP-API/WP-API/issues/2746 ## 2.0 Beta 13.0 (March 29, 2016) From f516a34e69b02475d1d07c872455d7a826174036 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Sat, 1 Oct 2016 11:00:32 -0400 Subject: [PATCH 158/318] Remove Scrutinzer We don't use this anymore, and it's an additional build step that we might as well save on. --- .scrutinizer.yml | 42 ------------------------------------------ README.md | 1 - 2 files changed, 43 deletions(-) delete mode 100644 .scrutinizer.yml diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index c6b9415b0c..0000000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,42 +0,0 @@ -# Scrutinizer Configuration File - -tools: - external_code_coverage: false - - php_sim: - enabled: true - min_mass: 50 - - php_pdepend: - enabled: true - configuration_file: null - suffixes: - - php - excluded_dirs: { } - - php_analyzer: - enabled: true - extensions: - - php - dependency_paths: { } - path_configs: { } - - php_changetracking: - enabled: true - bug_patterns: - - '\bfix(?:es|ed)?\b' - feature_patterns: - - '\badd(?:s|ed)?\b' - - '\bimplement(?:s|ed)?\b' - - php_hhvm: - enabled: true - command: hhvm - extensions: - - php - path_configs: { } - -before_commands: { } -after_commands: { } -artifacts: { } -build_failure_conditions: { } diff --git a/README.md b/README.md index d35818e633..72bf8428a0 100755 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ Access your WordPress site's data through an easy-to-use HTTP REST API. [![Build Status](https://travis-ci.org/WP-API/WP-API.svg?branch=develop)](https://travis-ci.org/WP-API/WP-API) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/WP-API/WP-API/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/WP-API/WP-API/?branch=develop) [![codecov.io](http://codecov.io/github/WP-API/WP-API/coverage.svg?branch=develop)](http://codecov.io/github/WP-API/WP-API?branch=develop) ## WARNING From 96caef7d8b8276aa2731a05ed3cc1071acad0812 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Sat, 1 Oct 2016 11:05:02 -0400 Subject: [PATCH 159/318] Update README to recomment usage of version 2. Now the `develop` branch and version 2 is considered stable and production ready, we can remove the big WARNING from the readme. --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d35818e633..046bf10edb 100755 --- a/README.md +++ b/README.md @@ -6,15 +6,12 @@ Access your WordPress site's data through an easy-to-use HTTP REST API. [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/WP-API/WP-API/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/WP-API/WP-API/?branch=develop) [![codecov.io](http://codecov.io/github/WP-API/WP-API/coverage.svg?branch=develop)](http://codecov.io/github/WP-API/WP-API?branch=develop) -## WARNING - -The **"develop"** branch is undergoing substantial changes and is **NOT -COMPLETE OR STABLE**. [Read the in-progress documentation](http://v2.wp-api.org/) +The **"develop"** branch is version 2 which is "beta" but stable and recommended for production. [Read the documentation](http://v2.wp-api.org/) to introduce yourself to endpoints, internal patterns, and implementation details. -The **"master"** branch represents a **BETA** of our next version release. +The **"master"** branch represents the **legacy** version of the REST API. -The latest **stable** version is available from the [WordPress Plugin Directory](https://wordpress.org/plugins/rest-api/). +The latest **stable** version is also available from the [WordPress Plugin Directory](https://wordpress.org/plugins/rest-api/). ## About From e9907d1c17d07db8039efc2b7de292c43f08fc18 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Sat, 1 Oct 2016 12:17:00 -0400 Subject: [PATCH 160/318] Add longer descriptions to important items --- CHANGELOG.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcbc48ff17..fd7020388b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,34 +2,40 @@ ## 2.0 Beta 14.0 (September 30, 2016) -- Password Protected Posts Support +- Add support for password protected posts + + Password protected posts are now fully supported, you can create edit and read password protected posts in the REST API. There is now a `protected` attribute in the `content` and `excerpt` fields in post response. + + To view password protected posts via the API, use the `password` query parameter to provide the post's password. (props @joehoyle, [#2720][gh-2720]) - Allow returning an error from field update callbacks + Fields added via `register_rest_field` can now return an instance of `WP_Error` in the `update_callback`. + (props @rmccue, [#2702][gh-2702]) -- Add "relevance" orderby to posts endpoint +- Update the wp-api.js client from the client-js repo. + + (props @joehoyle, [#2746][gh-2746]) + +- Add `relevance` `orderby` to posts endpoint (props @websupporter, [#2579][gh-2579]) -- Ability to orderby slug, email and url on users endpoints. +- Ability to order by `slug`, `email` and `url` on the users endpoints. (props @joehoyle, [#2721][gh-2721]) -- Add sticky parameter to the posts endpoint. +- Add `sticky` parameter to the posts endpoint. (props @joehoyle, [#2708][gh-2708]) -- #1612 Add link to comment children, allowing threaded comment querying +- Add link to comment children, allowing threaded comment querying (props @BE-Webdesign, [#2662][gh-2662], [#1612][gh-1612]) -- Update the wp-api.js client from the client-js repo. - - (props @joehoyle, [#2746][gh-2746]) - - Avoid unnecessary SQL query by passing `$user_nicename` (props @danielbachhuber, [#2435][gh-2435]) From 1babd6765f27fce4f65471bebdec0e4d1d938d3b Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Sat, 1 Oct 2016 13:05:08 -0400 Subject: [PATCH 161/318] Bump plugin.php to beta 14 --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index 803180b6d2..b071551c6e 100755 --- a/plugin.php +++ b/plugin.php @@ -4,7 +4,7 @@ * Description: JSON-based REST API for WordPress, originally developed as part of GSoC 2013. * Author: WP REST API Team * Author URI: http://v2.wp-api.org - * Version: 2.0-beta13 + * Version: 2.0-beta14 * Plugin URI: https://github.com/WP-API/WP-API * License: GPL2+ */ From 5b3b75ad1c1d32f65fdab24d616bcfe097360ff4 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Mon, 3 Oct 2016 12:23:51 -0400 Subject: [PATCH 162/318] Use get_comment_type() when comparing updating comment status Currently we don't allow updating a comment type, however in the update code we are checking the raw value vs get_comment_type() which means a comment of type "comment" can not be updated, as that's actually an empty value in the database. --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 35da2e0d03..01d0ed5920 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -432,7 +432,7 @@ public function update_item( $request ) { return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment id.' ), array( 'status' => 404 ) ); } - if ( isset( $request['type'] ) && $request['type'] !== $comment->comment_type ) { + if ( isset( $request['type'] ) && get_comment_type( $id ) !== $request['type'] ) { return new WP_Error( 'rest_comment_invalid_type', __( 'Sorry, you cannot change the comment type.' ), array( 'status' => 404 ) ); } From 2f96ca73ca9575008846c819975274805512c871 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Mon, 3 Oct 2016 19:09:43 -0400 Subject: [PATCH 163/318] Add ?{taxonomy}_exclude= query parameter This mirrors our existing support for `?{taxonomy}=` filtering in the posts controller (which allows querying for only records with are associated with any of the provided term IDs for the specified taxonomy) by adding an equivalent `_exclude` variant to list IDs of terms for which associated posts should NOT be returned. For categories _e.g._, `/wp/v2/posts?categories[]=7&categories[]=9` would return a post in categories 7 _or_ 9; this PR adds support for the inverse query `?categories_exclude[]=7&categories_exclude[]=9`, meaning "only return posts that are associated with neither category 7 or category 9." `{term base}_exclude` is supported for any taxonomy registered with the API, and is set within the same loop inside the controller as the existing term include parameters. While using the existing categories filter with a negative number was considered ([see slack discussion][slack-disc]), `{base}_exclude` maintains more consistency with other exclusion filters present in the API such as `parent_exclude` (and is much easier to implement, to boot). [slack-disc]: https://wordpress.slack.com/archives/core-restapi/p1475525429004981 --- .../class-wp-rest-posts-controller.php | 11 +++++ tests/test-rest-posts-controller.php | 45 ++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 47010b30d1..3fd40be681 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -171,6 +171,7 @@ public function get_items( $request ) { $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); foreach ( $taxonomies as $taxonomy ) { $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; + $tax_exclude = $base . '_exclude'; if ( ! empty( $request[ $base ] ) ) { $query_args['tax_query'][] = array( @@ -180,6 +181,16 @@ public function get_items( $request ) { 'include_children' => false, ); } + + if ( ! empty( $request[ $tax_exclude ] ) ) { + $query_args['tax_query'][] = array( + 'taxonomy' => $taxonomy->name, + 'field' => 'term_id', + 'terms' => $request[ $tax_exclude ], + 'include_children' => false, + 'operator' => 'NOT IN', + ); + } } $posts_query = new WP_Query(); diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 617d94ebae..c50fc6f3d0 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -335,7 +335,28 @@ public function test_get_items_tags_query() { $request->set_param( 'tags', array( $tag['term_id'] ) ); $response = $this->server->dispatch( $request ); - $this->assertCount( 1, $response->get_data() ); + $data = $response->get_data(); + $this->assertCount( 1, $data ); + $this->assertEquals( $id1, $data[0]['id'] ); + } + + public function test_get_items_tags_exclude_query() { + $id1 = $this->post_id; + $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id3 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id4 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $tag = wp_insert_term( 'My Tag', 'post_tag' ); + + wp_set_object_terms( $id1, array( $tag['term_id'] ), 'post_tag' ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'tags_exclude', array( $tag['term_id'] ) ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertCount( 3, $data ); + $this->assertEquals( $id4, $data[0]['id'] ); + $this->assertEquals( $id3, $data[1]['id'] ); + $this->assertEquals( $id2, $data[2]['id'] ); } public function test_get_items_tags_and_categories_query() { @@ -358,6 +379,28 @@ public function test_get_items_tags_and_categories_query() { $this->assertCount( 1, $response->get_data() ); } + public function test_get_items_tags_and_categories_exclude_query() { + $id1 = $this->post_id; + $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id3 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $id4 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); + $tag = wp_insert_term( 'My Tag', 'post_tag' ); + $category = wp_insert_term( 'My Category', 'category' ); + + wp_set_object_terms( $id1, array( $tag['term_id'] ), 'post_tag' ); + wp_set_object_terms( $id2, array( $tag['term_id'] ), 'post_tag' ); + wp_set_object_terms( $id1, array( $category['term_id'] ), 'category' ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'tags', array( $tag['term_id'] ) ); + $request->set_param( 'categories_exclude', array( $category['term_id'] ) ); + + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertCount( 1, $data ); + $this->assertEquals( $id2, $data[0]['id'] ); + } + public function test_get_items_sticky_query() { $id1 = $this->post_id; $id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) ); From 3a233ecf1a35b2ecb89ac903ab5fa3890fcaf8fb Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 5 Oct 2016 10:59:52 -0500 Subject: [PATCH 164/318] Match content handling logic from the Posts Controller and Core when sanitizing comment_content with kses - Allow the `comment_content` to be set in a request either by `content` or `content['raw']` - Sanitize the `comment_content` before saving to the database with `wp_filter_kses()` - Users with the `unfiltered_html` capability will have their comment_content sanitized with `wp_filter_post_kses()`. --- .../class-wp-rest-comments-controller.php | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index a624a4fa00..d37cf7a68b 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -318,11 +318,10 @@ public function create_item( $request ) { return new WP_Error( 'rest_comment_exists', __( 'Cannot create existing comment.' ), array( 'status' => 400 ) ); } - if ( empty( $request['content'] ) || ! is_string( $request['content'] ) ) { - return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); - } - $prepared_comment = $this->prepare_item_for_database( $request ); + if ( is_wp_error( $prepared_comment ) ) { + return $prepared_comment; + } // Setting remaining values before wp_insert_comment so we can // use wp_allow_comment(). @@ -436,11 +435,10 @@ public function update_item( $request ) { return new WP_Error( 'rest_comment_invalid_type', __( 'Sorry, you cannot change the comment type.' ), array( 'status' => 404 ) ); } - if ( isset( $request['content'] ) && ! is_string( $request['content'] ) ) { - return new WP_Error( 'rest_comment_content_invalid', __( 'Invalid comment content.' ), array( 'status' => 400 ) ); - } - $prepared_args = $this->prepare_item_for_database( $request ); + if ( is_wp_error( $prepared_args ) ) { + return $prepared_args; + } if ( empty( $prepared_args ) && isset( $request['status'] ) ) { // Only the comment status is being changed. @@ -736,8 +734,20 @@ protected function prepare_status_response( $comment_approved ) { protected function prepare_item_for_database( $request ) { $prepared_comment = array(); - if ( isset( $request['content'] ) ) { - $prepared_comment['comment_content'] = $request['content']; + if ( isset( $request['content'] ) && is_string( $request['content'] ) ) { + if ( current_user_can( 'unfiltered_html' ) ) { + $prepared_comment['comment_content'] = wp_filter_post_kses( $request['content'] ); + } else { + $prepared_comment['comment_content'] = wp_filter_kses( $request['content'] ); + } + } elseif ( isset( $request['content']['raw'] ) && is_string( $request['content']['raw'] ) ) { + if ( current_user_can( 'unfiltered_html' ) ) { + $prepared_comment['comment_content'] = wp_filter_post_kses( $request['content']['raw'] ); + } else { + $prepared_comment['comment_content'] = wp_filter_kses( $request['content']['raw'] ); + } + } else { + return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); } if ( isset( $request['post'] ) ) { From a0bc51fe7053ba7fa9daa677663dd94f2727879b Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 5 Oct 2016 12:00:42 -0500 Subject: [PATCH 165/318] Rework and attempt to simplify the logic for handling comment_content - Reorder and remove extra string checking on the `content` and `content.raw` properties - Return WP_Error for comment_content that is an empty string (matching `wp_handle_comment_submission()`) - Use the schema for `wp_filter_kses_post()` sanitization - Still add `wp_filter_kses()` sanitization for users without the unfiltered_html capability --- .../class-wp-rest-comments-controller.php | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index d37cf7a68b..13b2a3d087 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -734,21 +734,31 @@ protected function prepare_status_response( $comment_approved ) { protected function prepare_item_for_database( $request ) { $prepared_comment = array(); - if ( isset( $request['content'] ) && is_string( $request['content'] ) ) { - if ( current_user_can( 'unfiltered_html' ) ) { - $prepared_comment['comment_content'] = wp_filter_post_kses( $request['content'] ); - } else { - $prepared_comment['comment_content'] = wp_filter_kses( $request['content'] ); - } - } elseif ( isset( $request['content']['raw'] ) && is_string( $request['content']['raw'] ) ) { - if ( current_user_can( 'unfiltered_html' ) ) { - $prepared_comment['comment_content'] = wp_filter_post_kses( $request['content']['raw'] ); - } else { - $prepared_comment['comment_content'] = wp_filter_kses( $request['content']['raw'] ); - } + /** + * Allow the comment_content to be set via the 'content' or + * the 'content.raw' properties of the Request object. + */ + if ( isset( $request['content']['raw'] ) ) { + $prepared_comment['comment_content'] = $request['content']['raw']; + } elseif ( isset( $request['content'] ) ) { + $prepared_comment['comment_content'] = $request['content']; } else { return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); } + /** + * Do not allow comment_content to be an empty string. + * See `wp_handle_comment_submission()`. + */ + if ( '' === $prepared_comment['comment_content'] ) { + return new WP_Error( 'rest_comment_content_invalid', __( 'Comment content is invalid.' ), array( 'status' => 400 ) ); + } + /** + * Add additional sanitization for users without the 'unfiltered_html' + * capability. + */ + if ( ! current_user_can( 'unfiltered_html' ) ) { + $prepared_comment['comment_content'] = wp_filter_kses( $prepared_comment['comment_content'] ); + } if ( isset( $request['post'] ) ) { $prepared_comment['comment_post_ID'] = (int) $request['post']; @@ -879,7 +889,6 @@ public function get_item_schema() { ), 'arg_options' => array( 'sanitize_callback' => 'wp_filter_post_kses', - 'default' => '', ), ), 'date' => array( From b6afe0991caf99da762b2d593fd6a1b44e3710f8 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 5 Oct 2016 13:25:28 -0500 Subject: [PATCH 166/318] Only prevent creating comments with an empty string for the content --- .../class-wp-rest-comments-controller.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 13b2a3d087..07dfcb63c3 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -323,6 +323,15 @@ public function create_item( $request ) { return $prepared_comment; } + /** + * Do not allow a comment to be created with an empty string for + * comment_content. + * See `wp_handle_comment_submission()`. + */ + if ( '' === $prepared_comment['comment_content'] ) { + return new WP_Error( 'rest_comment_content_invalid', __( 'Comment content is invalid.' ), array( 'status' => 400 ) ); + } + // Setting remaining values before wp_insert_comment so we can // use wp_allow_comment(). if ( ! isset( $prepared_comment['comment_date_gmt'] ) ) { @@ -745,12 +754,6 @@ protected function prepare_item_for_database( $request ) { } else { return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); } - /** - * Do not allow comment_content to be an empty string. - * See `wp_handle_comment_submission()`. - */ - if ( '' === $prepared_comment['comment_content'] ) { - return new WP_Error( 'rest_comment_content_invalid', __( 'Comment content is invalid.' ), array( 'status' => 400 ) ); } /** * Add additional sanitization for users without the 'unfiltered_html' From 96c6da6ca7a9f0bc632e5d4ea4cffdc9bf3dfb36 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 5 Oct 2016 13:26:10 -0500 Subject: [PATCH 167/318] Handle special case where a comment can be updated and only change the status. --- lib/endpoints/class-wp-rest-comments-controller.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 07dfcb63c3..fb4911ec0d 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -751,15 +751,13 @@ protected function prepare_item_for_database( $request ) { $prepared_comment['comment_content'] = $request['content']['raw']; } elseif ( isset( $request['content'] ) ) { $prepared_comment['comment_content'] = $request['content']; - } else { - return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); - } } + /** * Add additional sanitization for users without the 'unfiltered_html' * capability. */ - if ( ! current_user_can( 'unfiltered_html' ) ) { + if ( ! current_user_can( 'unfiltered_html' ) && $prepared_comment['comment_content'] ) { $prepared_comment['comment_content'] = wp_filter_kses( $prepared_comment['comment_content'] ); } @@ -813,6 +811,12 @@ protected function prepare_item_for_database( $request ) { } } + // Require 'comment_content' unless only the 'comment_status' is being + // updated. + if ( ! empty( $prepared_comment ) && ! isset( $prepared_comment['comment_content'] ) ) { + return new WP_Error( 'rest_comment_content_required', __( 'Missing comment content.' ), array( 'status' => 400 ) ); + } + return apply_filters( 'rest_preprocess_comment', $prepared_comment, $request ); } From 5b7cba1477f742bed1bce8ca28de40ce0b9a7544 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 6 Oct 2016 11:35:57 -0400 Subject: [PATCH 168/318] Whitelist the allowed types in the settings controller --- .../class-wp-rest-settings-controller.php | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/endpoints/class-wp-rest-settings-controller.php b/lib/endpoints/class-wp-rest-settings-controller.php index fb16d25151..78e70042b0 100644 --- a/lib/endpoints/class-wp-rest-settings-controller.php +++ b/lib/endpoints/class-wp-rest-settings-controller.php @@ -56,21 +56,21 @@ public function get_item( $request ) { // Because get_option() is lossy, we have to // cast values to the type they are registered with. - $response[ $name ] = $this->cast_value_to_type( $response[ $name ], $args['schema']['type'] ); + $response[ $name ] = $this->prepare_value( $response[ $name ], $args['schema'] ); } return $response; } /** - * Convert a value to a given schema type. + * Prepare a value for output based off a schema array. * - * @param mixed $value - * @param string $type Type that the data should be converted to. + * @param mixed $value + * @param array $schema * @return mixed */ - protected function cast_value_to_type( $value, $type ) { - switch ( $type ) { + protected function prepare_value( $value, $schema ) { + switch ( $schema['type'] ) { case 'string': return strval( $value ); case 'number': @@ -78,7 +78,7 @@ protected function cast_value_to_type( $value, $type ) { case 'boolean': return (bool) $value; default: - return $value; + return null; } } @@ -147,6 +147,12 @@ protected function get_registered_options() { continue; } + // Whitelist the supported types for settings, as we don't want invalid types + // to be updated with arbitrary values that we can't do decent sanitizing for. + if ( ! in_array( $rest_args['schema']['type'], 'number', 'string', 'boolean', true ) ) { + continue; + } + $rest_options[ $rest_args['name'] ] = $rest_args; } From 2ee2a56b74bbda2b6b402942487f6afd600626d7 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 6 Oct 2016 11:36:12 -0400 Subject: [PATCH 169/318] Don't register settings on < 4.7 --- plugin.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugin.php b/plugin.php index c114538926..e7226b24d6 100755 --- a/plugin.php +++ b/plugin.php @@ -162,6 +162,10 @@ function _add_extra_api_taxonomy_arguments() { * once / if core starts to register settings internally. */ function rest_register_settings() { + global $wp_version; + if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { + return; + } require_once ABSPATH . 'wp-admin/includes/plugin.php'; register_setting( 'general', 'blogname', array( From 8ff5a74e82c8180ed27f99466721dff5fe4ac388 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 10:56:31 -0500 Subject: [PATCH 170/318] Remove the sanitization callback from the schema and add manually for comment_content. See https://github.com/WP-API/WP-API/issues/2758 --- lib/endpoints/class-wp-rest-comments-controller.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index fb4911ec0d..86637e8c2e 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -752,12 +752,9 @@ protected function prepare_item_for_database( $request ) { } elseif ( isset( $request['content'] ) ) { $prepared_comment['comment_content'] = $request['content']; } - - /** - * Add additional sanitization for users without the 'unfiltered_html' - * capability. - */ - if ( ! current_user_can( 'unfiltered_html' ) && $prepared_comment['comment_content'] ) { + if ( current_user_can( 'unfiltered_html' ) && $prepared_comment['comment_content'] ) { + $prepared_comment['comment_content'] = wp_filter_post_kses( $prepared_comment['comment_content'] ); + } elseif ( $prepared_comment['comment_content'] ) { $prepared_comment['comment_content'] = wp_filter_kses( $prepared_comment['comment_content'] ); } @@ -894,9 +891,6 @@ public function get_item_schema() { 'context' => array( 'view', 'edit', 'embed' ), ), ), - 'arg_options' => array( - 'sanitize_callback' => 'wp_filter_post_kses', - ), ), 'date' => array( 'description' => __( 'The date the object was published.' ), From fac5751135b2b4b3f99cf8394e52f97c40c41396 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 10:57:08 -0500 Subject: [PATCH 171/318] Update unit tests --- tests/test-rest-comments-controller.php | 45 ++++++++++++++++++++----- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index dcf4ef7eef..75cc58e602 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -654,7 +654,7 @@ public function test_create_item() { $this->assertEquals( $this->post_id, $data['post'] ); } - public function test_create_item_invalid_date() { + public function test_create_item_using_accepted_content_raw_value() { wp_set_current_user( 0 ); $params = array( @@ -662,8 +662,9 @@ public function test_create_item_invalid_date() { 'author_name' => 'Reverend Lovejoy', 'author_email' => 'lovejoy@example.com', 'author_url' => 'http://timothylovejoy.jr', - 'content' => "It\'s all over\, people! We don\'t have a prayer!", - 'date' => rand_str(), + 'content' => array( + 'raw' => 'Once something has been approved by the government, it\'s no longer immoral.', + ), ); $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); @@ -671,10 +672,13 @@ public function test_create_item_invalid_date() { $request->set_body( wp_json_encode( $params ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + $this->assertEquals( 201, $response->get_status() ); + + $data = $response->get_data(); + $this->assertEquals( $params['content']['raw'], $data['content']['raw'] ); } - public function test_create_item_invalid_content() { + public function test_create_item_invalid_blank_content() { wp_set_current_user( 0 ); $params = array( @@ -690,9 +694,30 @@ public function test_create_item_invalid_content() { $request->set_body( wp_json_encode( $params ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_comment_content_required', $response, 400 ); + $this->assertErrorResponse( 'rest_comment_content_invalid', $response, 400 ); + } + + public function test_create_item_invalid_date() { + wp_set_current_user( 0 ); + + $params = array( + 'post' => $this->post_id, + 'author_name' => 'Reverend Lovejoy', + 'author_email' => 'lovejoy@example.com', + 'author_url' => 'http://timothylovejoy.jr', + 'content' => "It\'s all over\, people! We don\'t have a prayer!", + 'date' => rand_str(), + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } + public function test_create_item_assign_different_user() { $subscriber_id = $this->factory->user->create( array( 'role' => 'subscriber', @@ -1122,6 +1147,7 @@ public function test_update_comment_date_gmt() { $params = array( 'date_gmt' => '2015-05-07T10:14:25', + 'content' => 'I\'ll be deep in the cold, cold ground before I recognize Missouri.', ); $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); $request->add_header( 'content-type', 'application/json' ); @@ -1150,18 +1176,18 @@ public function test_update_comment_invalid_type() { $this->assertErrorResponse( 'rest_comment_invalid_type', $response, 404 ); } - public function test_update_comment_invalid_content() { + public function test_update_comment_with_raw_property() { wp_set_current_user( $this->admin_id ); $params = array( - 'content' => array( 1, 2, 3 ), + 'content' => 'What the heck kind of name is Persephone?', ); $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); $request->add_header( 'content-type', 'application/json' ); $request->set_body( wp_json_encode( $params ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_comment_content_invalid', $response, 400 ); + $this->assertErrorResponse( 'rest_comment_content_required', $response, 400 ); } public function test_update_item_invalid_date() { @@ -1247,6 +1273,7 @@ public function test_update_comment_with_children_link() { // Change the comment parent. $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%s', $child_comment ) ); $request->set_param( 'parent', $comment_id_1 ); + $request->set_param( 'content', rand_str() ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); From 19575bf1980a753c0cab377e693324be2b1190f4 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 12:11:09 -0500 Subject: [PATCH 172/318] Add missing required `content` key to registered fields tests --- tests/test-rest-comments-controller.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 75cc58e602..7c1a7e5c33 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1443,7 +1443,7 @@ public function test_get_additional_field_registration() { $request = new WP_REST_Request( 'POST', '/wp/v2/comments/' . $this->approved_id ); $request->set_body_params(array( 'my_custom_int' => 123, - 'content' => 'abc', + 'content' => 'abc', )); wp_set_current_user( 1 ); @@ -1453,8 +1453,9 @@ public function test_get_additional_field_registration() { $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); $request->set_body_params(array( 'my_custom_int' => 123, - 'title' => 'hello', - 'post' => $this->post_id, + 'title' => 'hello', + 'content' => 'goodbye', + 'post' => $this->post_id, )); $response = $this->server->dispatch( $request ); From ce7c8e676879fee107ebdaef88b5610016cefa42 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 12:11:47 -0500 Subject: [PATCH 173/318] Correct bad logic in comment content.raw tests --- tests/test-rest-comments-controller.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 7c1a7e5c33..331e3bcde9 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -675,7 +675,8 @@ public function test_create_item_using_accepted_content_raw_value() { $this->assertEquals( 201, $response->get_status() ); $data = $response->get_data(); - $this->assertEquals( $params['content']['raw'], $data['content']['raw'] ); + $new_comment = get_comment( $data['id'] ); + $this->assertEquals( $params['content']['raw'], $new_comment->comment_content ); } public function test_create_item_invalid_blank_content() { @@ -1180,14 +1181,21 @@ public function test_update_comment_with_raw_property() { wp_set_current_user( $this->admin_id ); $params = array( - 'content' => 'What the heck kind of name is Persephone?', + 'content' => array( + 'raw' => 'What the heck kind of name is Persephone?', + ), ); $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/comments/%d', $this->approved_id ) ); $request->add_header( 'content-type', 'application/json' ); $request->set_body( wp_json_encode( $params ) ); $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_comment_content_required', $response, 400 ); + + $this->assertEquals( 200, $response->get_status() ); + + $comment = $response->get_data(); + $updated = get_comment( $this->approved_id ); + $this->assertEquals( $params['content']['raw'], $updated->comment_content ); } public function test_update_item_invalid_date() { From d2243378d9b93599a4b639e66125d994b2bcf45f Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 6 Oct 2016 13:25:10 -0400 Subject: [PATCH 174/318] Fix in_array call --- lib/endpoints/class-wp-rest-settings-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-settings-controller.php b/lib/endpoints/class-wp-rest-settings-controller.php index 78e70042b0..652d556db3 100644 --- a/lib/endpoints/class-wp-rest-settings-controller.php +++ b/lib/endpoints/class-wp-rest-settings-controller.php @@ -149,7 +149,7 @@ protected function get_registered_options() { // Whitelist the supported types for settings, as we don't want invalid types // to be updated with arbitrary values that we can't do decent sanitizing for. - if ( ! in_array( $rest_args['schema']['type'], 'number', 'string', 'boolean', true ) ) { + if ( ! in_array( $rest_args['schema']['type'], array( 'number', 'string', 'boolean' ), true ) ) { continue; } From 44cdd89ffdc485cee0cbb7aa8fb8587891132ec4 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 12:28:38 -0500 Subject: [PATCH 175/318] Change conditional check on the existence of $prepared_comment['comment_content'] to use `isset()` --- lib/endpoints/class-wp-rest-comments-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 86637e8c2e..d30dc22609 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -752,9 +752,9 @@ protected function prepare_item_for_database( $request ) { } elseif ( isset( $request['content'] ) ) { $prepared_comment['comment_content'] = $request['content']; } - if ( current_user_can( 'unfiltered_html' ) && $prepared_comment['comment_content'] ) { + if ( current_user_can( 'unfiltered_html' ) && isset( $prepared_comment['comment_content'] ) ) { $prepared_comment['comment_content'] = wp_filter_post_kses( $prepared_comment['comment_content'] ); - } elseif ( $prepared_comment['comment_content'] ) { + } elseif ( isset( $prepared_comment['comment_content'] ) ) { $prepared_comment['comment_content'] = wp_filter_kses( $prepared_comment['comment_content'] ); } From 7b2911ed5ecbc5dcb107420998fe887eedfc13a7 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 6 Oct 2016 13:29:33 -0400 Subject: [PATCH 176/318] No longer necessary to inlcude the admin file as register_setting has moved. --- lib/endpoints/class-wp-rest-settings-controller.php | 1 - plugin.php | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-settings-controller.php b/lib/endpoints/class-wp-rest-settings-controller.php index 652d556db3..da833cea28 100644 --- a/lib/endpoints/class-wp-rest-settings-controller.php +++ b/lib/endpoints/class-wp-rest-settings-controller.php @@ -114,7 +114,6 @@ public function update_item( $request ) { * @return array */ protected function get_registered_options() { - require_once ABSPATH . 'wp-admin/includes/plugin.php'; $rest_options = array(); foreach ( get_registered_settings() as $name => $args ) { diff --git a/plugin.php b/plugin.php index e7226b24d6..2aa1ce91ad 100755 --- a/plugin.php +++ b/plugin.php @@ -166,7 +166,6 @@ function rest_register_settings() { if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { return; } - require_once ABSPATH . 'wp-admin/includes/plugin.php'; register_setting( 'general', 'blogname', array( 'show_in_rest' => array( From 5b6ca785ea24e45acc5889894081cb3c0d1d60c4 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 6 Oct 2016 13:32:13 -0400 Subject: [PATCH 177/318] Only include the settings controller on 4.7 --- plugin.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index 2aa1ce91ad..484395676d 100755 --- a/plugin.php +++ b/plugin.php @@ -83,7 +83,10 @@ * WP_REST_Settings_Controller class. */ if ( ! class_exists( 'WP_REST_Settings_Controller' ) ) { - require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-settings-controller.php'; + global $wp_version; + if ( version_compare( $wp_version, '4.7-alpha', '>=' ) ) { + require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-settings-controller.php'; + } } /** From ad6aff5de58789605a94587bf0ab3c6130dde9f0 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 6 Oct 2016 13:48:08 -0400 Subject: [PATCH 178/318] Move the version check to the init of the class --- plugin.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugin.php b/plugin.php index 484395676d..7968ddb7fe 100755 --- a/plugin.php +++ b/plugin.php @@ -83,10 +83,7 @@ * WP_REST_Settings_Controller class. */ if ( ! class_exists( 'WP_REST_Settings_Controller' ) ) { - global $wp_version; - if ( version_compare( $wp_version, '4.7-alpha', '>=' ) ) { - require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-settings-controller.php'; - } + require_once dirname( __FILE__ ) . '/lib/endpoints/class-wp-rest-settings-controller.php'; } /** @@ -333,9 +330,12 @@ function create_initial_rest_routes() { $controller = new WP_REST_Comments_Controller; $controller->register_routes(); - // Settings. - $controller = new WP_REST_Settings_Controller; - $controller->register_routes(); + // Settings. 4.7+ only. + global $wp_version; + if ( version_compare( $wp_version, '4.7-alpha', '>=' ) ) { + $controller = new WP_REST_Settings_Controller; + $controller->register_routes(); + } } } From 61587a40f84a5df9cd725512fae693f338159b0d Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 6 Oct 2016 14:08:11 -0400 Subject: [PATCH 179/318] Set the default to comment for type --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 01d0ed5920..5aa186eb21 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -920,9 +920,9 @@ public function get_item_schema() { 'description' => __( 'Type of Comment for the object.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), + 'default' => 'comment', 'arg_options' => array( 'sanitize_callback' => 'sanitize_key', - 'default' => '', ), ), ), From 3604724d21377f2066a666acababffabdb937273 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Thu, 6 Oct 2016 14:18:04 -0400 Subject: [PATCH 180/318] Comments have to be inserted with type ='' for "comment" type --- lib/endpoints/class-wp-rest-comments-controller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 5aa186eb21..52692c31bc 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -765,7 +765,8 @@ protected function prepare_item_for_database( $request ) { } if ( isset( $request['type'] ) ) { - $prepared_comment['comment_type'] = $request['type']; + // Comment type "comment" needs to be created as an empty string. + $prepared_comment['comment_type'] = 'comment' === $request['type'] ? '' : $request['type']; } if ( isset( $request['karma'] ) ) { From d193b3ba1d0c1570ed9d05f935cb1850f8fd0089 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 6 Oct 2016 14:41:43 -0400 Subject: [PATCH 181/318] Add fields classes --- .../class-wp-rest-comments-controller.php | 24 ++ .../class-wp-rest-posts-controller.php | 29 ++ .../class-wp-rest-terms-controller.php | 22 ++ .../class-wp-rest-users-controller.php | 24 +- .../class-wp-rest-comment-meta-fields.php | 21 ++ lib/fields/class-wp-rest-meta-fields.php | 331 ++++++++++++++++++ lib/fields/class-wp-rest-post-meta-fields.php | 37 ++ lib/fields/class-wp-rest-term-meta-fields.php | 36 ++ lib/fields/class-wp-rest-user-meta-fields.php | 21 ++ plugin.php | 35 ++ 10 files changed, 579 insertions(+), 1 deletion(-) create mode 100644 lib/fields/class-wp-rest-comment-meta-fields.php create mode 100644 lib/fields/class-wp-rest-meta-fields.php create mode 100644 lib/fields/class-wp-rest-post-meta-fields.php create mode 100644 lib/fields/class-wp-rest-term-meta-fields.php create mode 100644 lib/fields/class-wp-rest-user-meta-fields.php diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 1321281fcf..268232a910 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -8,6 +8,8 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'comments'; + + $this->meta = new WP_REST_Comment_Meta_Fields(); } /** @@ -370,6 +372,14 @@ public function create_item( $request ) { $this->handle_status_param( $request['status'], $comment ); } + $schema = $this->get_item_schema(); + if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { + $meta_update = $this->meta->update_value( $request['meta'], $comment_id ); + if ( is_wp_error( $meta_update ) ) { + return $meta_update; + } + } + $comment = get_comment( $comment_id ); $fields_update = $this->update_additional_fields_for_object( $comment, $request ); if ( is_wp_error( $fields_update ) ) { @@ -453,6 +463,14 @@ public function update_item( $request ) { } } + $schema = $this->get_item_schema(); + if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { + $meta_update = $this->meta->update_value( $request['meta'], $id ); + if ( is_wp_error( $meta_update ) ) { + return $meta_update; + } + } + $comment = get_comment( $id ); $fields_update = $this->update_additional_fields_for_object( $comment, $request ); if ( is_wp_error( $fields_update ) ) { @@ -581,6 +599,10 @@ public function prepare_item_for_response( $comment, $request ) { $data['author_avatar_urls'] = rest_get_avatar_urls( $comment->comment_author_email ); } + if ( ! empty( $schema['properties']['meta'] ) ) { + $data['meta'] = $this->meta->get_value( $comment->comment_ID, $request ); + } + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); $data = $this->filter_response_by_context( $data, $context ); @@ -946,6 +968,8 @@ public function get_item_schema() { ); } + $schema['properties']['meta'] = $this->meta->get_field_schema(); + return $this->add_additional_fields_schema( $schema ); } diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 6759065751..491cfbfb1a 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -9,6 +9,8 @@ public function __construct( $post_type ) { $this->namespace = 'wp/v2'; $obj = get_post_type_object( $post_type ); $this->rest_base = ! empty( $obj->rest_base ) ? $obj->rest_base : $obj->name; + + $this->meta = new WP_REST_Post_Meta_Fields( $this->post_type ); } /** @@ -413,6 +415,13 @@ public function create_item( $request ) { } $post = $this->get_post( $post_id ); + if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { + $meta_update = $this->meta->update_value( $request['meta'], (int) $request['id'] ); + if ( is_wp_error( $meta_update ) ) { + return $meta_update; + } + } + $fields_update = $this->update_additional_fields_for_object( $post, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; @@ -519,6 +528,14 @@ public function update_item( $request ) { } $post = $this->get_post( $post_id ); + + if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { + $meta_update = $this->meta->update_value( $request['meta'], $post->ID ); + if ( is_wp_error( $meta_update ) ) { + return $meta_update; + } + } + $fields_update = $this->update_additional_fields_for_object( $post, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; @@ -1251,6 +1268,10 @@ public function prepare_item_for_response( $post, $request ) { } } + if ( ! empty( $schema['properties']['meta'] ) ) { + $data['meta'] = $this->meta->get_value( $post->ID, $request ); + } + $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); foreach ( $taxonomies as $taxonomy ) { $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; @@ -1509,6 +1530,7 @@ public function get_item_schema() { 'revisions', 'page-attributes', 'post-formats', + 'custom-fields', ); $fixed_schemas = array( 'post' => array( @@ -1520,6 +1542,7 @@ public function get_item_schema() { 'comments', 'revisions', 'post-formats', + 'custom-fields', ), 'page' => array( 'title', @@ -1530,12 +1553,14 @@ public function get_item_schema() { 'comments', 'revisions', 'page-attributes', + 'custom-fields', ), 'attachment' => array( 'title', 'author', 'comments', 'revisions', + 'custom-fields', ), ); foreach ( $post_type_attributes as $attribute ) { @@ -1670,6 +1695,10 @@ public function get_item_schema() { ); break; + case 'custom-fields': + $schema['properties']['meta'] = $this->meta->get_field_schema(); + break; + } } diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 0c3fcfede2..f963b5a0fc 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -15,6 +15,8 @@ public function __construct( $taxonomy ) { $this->namespace = 'wp/v2'; $tax_obj = get_taxonomy( $taxonomy ); $this->rest_base = ! empty( $tax_obj->rest_base ) ? $tax_obj->rest_base : $tax_obj->name; + + $this->meta = new WP_REST_Term_Meta_Fields( $taxonomy ); } /** @@ -371,6 +373,13 @@ public function create_item( $request ) { */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, true ); + if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { + $meta_update = $this->meta->update_value( $request['meta'], (int) $request['id'] ); + if ( is_wp_error( $meta_update ) ) { + return $meta_update; + } + } + $fields_update = $this->update_additional_fields_for_object( $term, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; @@ -445,6 +454,14 @@ public function update_item( $request ) { /* This action is documented in lib/endpoints/class-wp-rest-terms-controller.php */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, false ); + $schema = $this->get_item_schema(); + if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { + $meta_update = $this->meta->update_value( $request['meta'], (int) $request['id'] ); + if ( is_wp_error( $meta_update ) ) { + return $meta_update; + } + } + $fields_update = $this->update_additional_fields_for_object( $term, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; @@ -593,6 +610,9 @@ public function prepare_item_for_response( $item, $request ) { if ( ! empty( $schema['properties']['parent'] ) ) { $data['parent'] = (int) $item->parent; } + if ( ! empty( $schema['properties']['meta'] ) ) { + $data['meta'] = $this->meta->get_value( $item->term_id, $request ); + } $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->add_additional_fields_to_object( $data, $request ); @@ -739,6 +759,8 @@ public function get_item_schema() { 'context' => array( 'view', 'edit' ), ); } + + $schema['properties']['meta'] = $this->meta->get_field_schema(); return $this->add_additional_fields_schema( $schema ); } diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index b5e1429864..255f31fcc4 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -8,6 +8,8 @@ class WP_REST_Users_Controller extends WP_REST_Controller { public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'users'; + + $this->meta = new WP_REST_User_Meta_Fields(); } /** @@ -329,6 +331,13 @@ public function create_item( $request ) { array_map( array( $user, 'add_role' ), $request['roles'] ); } + if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { + $meta_update = $this->meta->update_value( $request['meta'], $user_id ); + if ( is_wp_error( $meta_update ) ) { + return $meta_update; + } + } + $fields_update = $this->update_additional_fields_for_object( $user, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; @@ -421,6 +430,14 @@ public function update_item( $request ) { array_map( array( $user, 'add_role' ), $request['roles'] ); } + $schema = $this->get_item_schema(); + if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { + $meta_update = $this->meta->update_value( $request['meta'], $id ); + if ( is_wp_error( $meta_update ) ) { + return $meta_update; + } + } + $fields_update = $this->update_additional_fields_for_object( $user, $request ); if ( is_wp_error( $fields_update ) ) { return $fields_update; @@ -579,6 +596,10 @@ public function prepare_item_for_response( $user, $request ) { $data['avatar_urls'] = rest_get_avatar_urls( $user->user_email ); } + if ( ! empty( $schema['properties']['meta'] ) ) { + $data['meta'] = $this->meta->get_value( $user->ID, $request ); + } + $context = ! empty( $request['context'] ) ? $request['context'] : 'embed'; $data = $this->add_additional_fields_to_object( $data, $request ); @@ -866,9 +887,10 @@ public function get_item_schema() { 'readonly' => true, 'properties' => $avatar_properties, ); - } + $schema['properties']['meta'] = $this->meta->get_field_schema(); + return $this->add_additional_fields_schema( $schema ); } diff --git a/lib/fields/class-wp-rest-comment-meta-fields.php b/lib/fields/class-wp-rest-comment-meta-fields.php new file mode 100644 index 0000000000..15d89dafd4 --- /dev/null +++ b/lib/fields/class-wp-rest-comment-meta-fields.php @@ -0,0 +1,21 @@ +get_rest_field_type(), 'meta', array( + 'get_callback' => array( $this, 'get_value' ), + 'update_callback' => array( $this, 'update_value' ), + 'schema' => $this->get_field_schema(), + )); + } + + /** + * Get the `meta` field value. + * + * @param int $id Object ID to fetch meta for. + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|array + */ + public function get_value( $id, $request ) { + $fields = $this->get_registered_fields(); + $response = array(); + + foreach ( $fields as $name => $args ) { + $all_values = get_metadata( $this->get_meta_type(), $id, $name, false ); + if ( $args['single'] ) { + if ( empty( $all_values ) ) { + $value = $args['schema']['default']; + } else { + $value = $all_values[0]; + } + $value = $this->prepare_value_for_response( $value, $request, $args ); + } else { + $value = array(); + foreach ( $all_values as $row ) { + $value[] = $this->prepare_value_for_response( $row, $request, $args ); + } + } + + $response[ $name ] = $value; + } + + return (object) $response; + } + + /** + * Prepare value for response. + * + * This is required because some native types cannot be stored correctly in + * the database, such as booleans. We need to cast back to the relevant type + * before passing back to JSON. + * + * @param mixed $value Value to prepare. + * @param WP_REST_Request $request Current request object. + * @param array $args Options for the field. + * @return mixed Prepared value. + */ + protected function prepare_value_for_response( $value, $request, $args ) { + if ( ! empty( $args['prepare_callback'] ) ) { + $value = call_user_func( $args['prepare_callback'], $value, $request, $args ); + } + + return $value; + } + + /** + * Update meta values. + * + * @param WP_REST_Request $request Full detail about the request. + * @return WP_Error|null Error if one occurs, null on success. + */ + public function update_value( $params, $id ) { + $fields = $this->get_registered_fields(); + + foreach ( $fields as $name => $args ) { + if ( ! array_key_exists( $name, $params ) ) { + continue; + } + + // A null value means reset the field, which is essentially deleting it + // from the database and then relying on the default value. + if ( is_null( $params[ $name ] ) ) { + $result = $this->delete_meta_value( $id, $name ); + } elseif ( $args['single'] ) { + $result = $this->update_meta_value( $id, $name, $params[ $name ] ); + } else { + $result = $this->update_multi_meta_value( $id, $name, $params[ $name ] ); + } + + if ( is_wp_error( $result ) ) { + return $result; + } + } + + return null; + } + + /** + * Delete meta value for an object. + * + * @param int $object Object ID the field belongs to. + * @param string $name Key for the field. + * @return bool|WP_Error True if meta field is deleted, error otherwise. + */ + protected function delete_meta_value( $object, $name ) { + if ( ! current_user_can( 'delete_post_meta', $object, $name ) ) { + return new WP_Error( + 'rest_cannot_delete', + sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ), + array( 'key' => $name, 'status' => rest_authorization_required_code() ) + ); + } + + if ( ! delete_metadata( $this->get_meta_type(), $object, wp_slash( $name ) ) ) { + return new WP_Error( + 'rest_meta_database_error', + __( 'Could not delete meta value from database.' ), + array( 'key' => $name, 'status' => WP_HTTP::INTERNAL_SERVER_ERROR ) + ); + } + + return true; + } + + /** + * Update multiple meta values for an object. + * + * Alters the list of values in the database to match the list of provided values. + * + * @param int $object Object ID. + * @param string $name Key for the custom field. + * @param array $values List of values to update to. + * @return bool|WP_Error True if meta fields are updated, error otherwise. + */ + protected function update_multi_meta_value( $object, $name, $values ) { + if ( ! current_user_can( 'edit_post_meta', $object, $name ) ) { + return new WP_Error( + 'rest_cannot_update', + sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ), + array( 'key' => $name, 'status' => rest_authorization_required_code() ) + ); + } + + $current = get_metadata( $this->get_meta_type(), $object, $name, false ); + $to_add = array_diff( $values, $current ); + $to_remove = array_diff( $current, $values ); + + foreach ( $to_add as $value ) { + if ( ! add_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { + return new WP_Error( + 'rest_meta_database_error', + __( 'Could not update meta value in database.' ), + array( 'key' => $name, 'status' => WP_HTTP::INTERNAL_SERVER_ERROR ) + ); + } + } + foreach ( $to_remove as $value ) { + if ( ! delete_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { + return new WP_Error( + 'rest_meta_database_error', + __( 'Could not update meta value in database.' ), + array( 'key' => $name, 'status' => WP_HTTP::INTERNAL_SERVER_ERROR ) + ); + } + } + + return true; + } + + /** + * Update meta value for an object. + * + * @param int $object Object ID. + * @param string $name Key for the custom field. + * @return bool|WP_Error True if meta field is deleted, error otherwise. + */ + protected function update_meta_value( $object, $name, $value ) { + if ( ! current_user_can( 'edit_post_meta', $object, $name ) ) { + return new WP_Error( + 'rest_cannot_update', + sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ), + array( 'key' => $name, 'status' => rest_authorization_required_code() ) + ); + } + + if ( ! update_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { + return new WP_Error( + 'rest_meta_database_error', + __( 'Could not update meta value in database.' ), + array( 'key' => $name, 'status' => WP_HTTP::INTERNAL_SERVER_ERROR ) + ); + } + + return true; + } + + /** + * Get all the registered meta fields. + * + * @return array + */ + protected function get_registered_fields() { + $registered = array(); + + foreach ( get_registered_meta_keys( $this->get_meta_type() ) as $name => $args ) { + if ( empty( $args['show_in_rest'] ) ) { + continue; + } + + $rest_args = array(); + if ( is_array( $args['show_in_rest'] ) ) { + $rest_args = $args['show_in_rest']; + } + + $default_args = array( + 'name' => $name, + 'single' => $args['single'], + 'schema' => array(), + 'prepare_callback' => array( $this, 'prepare_value' ), + ); + $default_schema = array( + 'type' => null, + 'description' => empty( $args['description'] ) ? '' : $args['description'], + 'default' => isset( $args['default'] ) ? $args['default'] : null, + ); + $rest_args = array_merge( $default_args, $rest_args ); + $rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] ); + + if ( empty( $rest_args['schema']['type'] ) ) { + // Skip over meta fields that don't have a defined type + if ( empty( $args['type'] ) ) { + continue; + } + + if ( $rest_args['single'] ) { + $rest_args['schema']['type'] = $args['type']; + } else { + $rest_args['schema']['type'] = 'array'; + $rest_args['schema']['items'] = array( + 'type' => $args['type'], + ); + } + } + + $registered[ $rest_args['name'] ] = $rest_args; + } + + return $registered; + } + + /** + * Get the object's `meta` schema, conforming to JSON Schema. + * + * @return array + */ + public function get_field_schema() { + $fields = $this->get_registered_fields(); + + $schema = array( + 'description' => __( 'Meta fields.' ), + 'type' => 'object', + 'context' => array( 'view', 'edit' ), + 'properties' => array(), + ); + + foreach ( $fields as $key => $args ) { + $schema['properties'][ $key ] = $args['schema']; + } + + return $schema; + } + + /** + * Prepare a meta value for output. + * + * Default preparation for meta fields. Override by passing the + * `prepare_callback` in your `show_in_rest` options. + * + * @param mixed $value Meta value from the database. + * @param WP_REST_Request $request Request object. + * @param array $args REST-specific options for the meta key. + * @return mixed Value prepared for output. + */ + public static function prepare_value( $value, $request, $args ) { + $type = $args['schema']['type']; + + // For multi-value fields, check the item type instead. + if ( 'array' === $type && ! empty( $args['schema']['items']['type'] ) ) { + $type = $args['schema']['items']['type']; + } + + switch ( $type ) { + case 'string': + $value = strval( $value ); + break; + case 'number': + $value = floatval( $value ); + break; + case 'boolean': + $value = (bool) $value; + break; + } + + // Don't allow objects to be output. + if ( is_object( $value ) && ! ( $value instanceof JsonSerializable ) ) { + return null; + } + + return $value; + } +} diff --git a/lib/fields/class-wp-rest-post-meta-fields.php b/lib/fields/class-wp-rest-post-meta-fields.php new file mode 100644 index 0000000000..c16460374f --- /dev/null +++ b/lib/fields/class-wp-rest-post-meta-fields.php @@ -0,0 +1,37 @@ +post_type = $post_type; + } + + /** + * Get the object type for meta. + * + * @return string + */ + protected function get_meta_type() { + return 'post'; + } + + /** + * Get the type for `register_rest_field`. + * + * @return string Custom post type slug. + */ + public function get_rest_field_type() { + return $this->post_type; + } +} diff --git a/lib/fields/class-wp-rest-term-meta-fields.php b/lib/fields/class-wp-rest-term-meta-fields.php new file mode 100644 index 0000000000..6c4e96224c --- /dev/null +++ b/lib/fields/class-wp-rest-term-meta-fields.php @@ -0,0 +1,36 @@ +taxonomy = $taxonomy; + } + + /** + * Get the object type for meta. + * + * @return string + */ + protected function get_meta_type() { + return 'term'; + } + + /** + * Get the type for `register_rest_field`. + * + * @return string + */ + public function get_rest_field_type() { + return 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy; + } +} diff --git a/lib/fields/class-wp-rest-user-meta-fields.php b/lib/fields/class-wp-rest-user-meta-fields.php new file mode 100644 index 0000000000..2e25c7e4e4 --- /dev/null +++ b/lib/fields/class-wp-rest-user-meta-fields.php @@ -0,0 +1,21 @@ + Date: Thu, 6 Oct 2016 14:45:50 -0400 Subject: [PATCH 182/318] Add tests from fields repo --- tests/test-rest-post-meta-fields.php | 574 +++++++++++++++++++++++++++ 1 file changed, 574 insertions(+) create mode 100644 tests/test-rest-post-meta-fields.php diff --git a/tests/test-rest-post-meta-fields.php b/tests/test-rest-post-meta-fields.php new file mode 100644 index 0000000000..9de32dfb0f --- /dev/null +++ b/tests/test-rest-post-meta-fields.php @@ -0,0 +1,574 @@ + true, + 'single' => true, + )); + register_meta( 'post', 'test_multi', array( + 'show_in_rest' => true, + 'single' => false, + )); + register_meta( 'post', 'test_bad_auth', array( + 'show_in_rest' => true, + 'single' => true, + 'auth_callback' => '__return_false', + )); + register_meta( 'post', 'test_bad_auth_multi', array( + 'show_in_rest' => true, + 'single' => false, + 'auth_callback' => '__return_false', + )); + register_meta( 'post', 'test_no_rest', array() ); + register_meta( 'post', 'test_rest_disabled', array( + 'show_in_rest' => false, + )); + register_meta( 'post', 'test_custom_schema', array( + 'single' => true, + 'type' => 'integer', + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'number', + ), + ), + )); + register_meta( 'post', 'test_invalid_type', array( + 'single' => true, + 'type' => false, + 'show_in_rest' => true, + )); + + /** @var WP_REST_Server $wp_rest_server */ + global $wp_rest_server; + $this->server = $wp_rest_server = new WP_Test_Spy_REST_Server; + do_action( 'rest_api_init' ); + + $this->post_id = $this->factory->post->create(); + } + + protected function grant_write_permission() { + // Ensure we have write permission. + $user = $this->factory->user->create( array( + 'role' => 'editor', + )); + wp_set_current_user( $user ); + } + + public function test_get_value() { + add_post_meta( $this->post_id, 'test_single', 'testvalue' ); + + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $this->assertArrayHasKey( 'meta', $data ); + + $meta = (array) $data['meta']; + $this->assertArrayHasKey( 'test_single', $meta ); + $this->assertEquals( 'testvalue', $meta['test_single'] ); + } + + /** + * @depends test_get_value + */ + public function test_get_multi_value() { + add_post_meta( $this->post_id, 'test_multi', 'value1' ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayHasKey( 'test_multi', $meta ); + $this->assertInternalType( 'array', $meta['test_multi'] ); + $this->assertContains( 'value1', $meta['test_multi'] ); + + // Check after an update. + add_post_meta( $this->post_id, 'test_multi', 'value2' ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertContains( 'value1', $meta['test_multi'] ); + $this->assertContains( 'value2', $meta['test_multi'] ); + } + + /** + * @depends test_get_value + */ + public function test_get_unregistered() { + add_post_meta( $this->post_id, 'test_unregistered', 'value1' ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayNotHasKey( 'test_unregistered', $meta ); + } + + /** + * @depends test_get_value + */ + public function test_get_registered_no_api_access() { + add_post_meta( $this->post_id, 'test_no_rest', 'for_the_wicked' ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayNotHasKey( 'test_no_rest', $meta ); + } + + /** + * @depends test_get_value + */ + public function test_get_registered_api_disabled() { + add_post_meta( $this->post_id, 'test_rest_disabled', 'sleepless_nights' ); + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayNotHasKey( 'test_rest_disabled', $meta ); + } + + public function test_get_value_types() { + register_meta( 'post', 'test_string', array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'string', + )); + register_meta( 'post', 'test_number', array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'number', + )); + register_meta( 'post', 'test_bool', array( + 'show_in_rest' => true, + 'single' => true, + 'type' => 'boolean', + )); + + /** @var WP_REST_Server $wp_rest_server */ + global $wp_rest_server; + $this->server = $wp_rest_server = new WP_Test_Spy_REST_Server; + do_action( 'rest_api_init' ); + + add_post_meta( $this->post_id, 'test_string', 42 ); + add_post_meta( $this->post_id, 'test_number', '42' ); + add_post_meta( $this->post_id, 'test_bool', 1 ); + + $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + + $this->assertArrayHasKey( 'test_string', $meta ); + $this->assertInternalType( 'string', $meta['test_string'] ); + $this->assertSame( '42', $meta['test_string'] ); + + $this->assertArrayHasKey( 'test_number', $meta ); + $this->assertInternalType( 'float', $meta['test_number'] ); + $this->assertSame( 42.0, $meta['test_number'] ); + + $this->assertArrayHasKey( 'test_bool', $meta ); + $this->assertInternalType( 'boolean', $meta['test_bool'] ); + $this->assertSame( true, $meta['test_bool'] ); + } + + /** + * @depends test_get_value + */ + public function test_set_value() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_single', false ); + $this->assertEmpty( $values ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_single' => 'test_value', + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_single', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 1, $meta ); + $this->assertEquals( 'test_value', $meta[0] ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayHasKey( 'test_single', $meta ); + $this->assertEquals( 'test_value', $meta['test_single'] ); + } + + /** + * @depends test_set_value + */ + public function test_set_value_unauthenticated() { + $data = array( + 'meta' => array( + 'test_single' => 'test_value', + ), + ); + + wp_set_current_user( 0 ); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); + + // Check that the value wasn't actually updated. + $this->assertEmpty( get_post_meta( $this->post_id, 'test_single', false ) ); + } + + /** + * @depends test_set_value + */ + public function test_set_value_blocked() { + $data = array( + 'meta' => array( + 'test_bad_auth' => 'test_value', + ), + ); + + $this->grant_write_permission(); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); + $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth', false ) ); + } + + /** + * @depends test_set_value + */ + public function test_set_value_db_error() { + $data = array( + 'meta' => array( + 'test_single' => 'test_value', + ), + ); + + $this->grant_write_permission(); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + /** + * Disable showing error as the below is going to intentionally + * trigger a DB error. + */ + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_insert_query' ) ); + + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_insert_query' ) ); + $wpdb->show_errors = true; + } + + public function test_set_value_multiple() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_multi' => array( 'val1' ), + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 1, $meta ); + $this->assertEquals( 'val1', $meta[0] ); + + // Add another value. + $data = array( + 'meta' => array( + 'test_multi' => array( 'val1', 'val2' ), + ), + ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 2, $meta ); + $this->assertContains( 'val1', $meta ); + $this->assertContains( 'val2', $meta ); + } + + /** + * @depends test_set_value_multiple + */ + public function test_set_value_multiple_unauthenticated() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); + + wp_set_current_user( 0 ); + + $data = array( + 'meta' => array( + 'test_multi' => array( 'val1' ), + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); + + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $meta ); + } + + /** + * @depends test_set_value_multiple + */ + public function test_set_value_multiple_blocked() { + $data = array( + 'meta' => array( + 'test_bad_auth_multi' => array( 'test_value' ), + ), + ); + + $this->grant_write_permission(); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); + $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth_multi', false ) ); + } + + public function test_add_multi_value_db_error() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_multi' => array( 'val1' ), + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + /** + * Disable showing error as the below is going to intentionally + * trigger a DB error. + */ + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_insert_query' ) ); + + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_insert_query' ) ); + $wpdb->show_errors = true; + + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } + + public function test_remove_multi_value_db_error() { + add_post_meta( $this->post_id, 'test_multi', 'val1' ); + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEquals( array( 'val1' ), $values ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_multi' => array(), + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + /** + * Disable showing error as the below is going to intentionally + * trigger a DB error. + */ + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_delete_query' ) ); + + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_delete_query' ) ); + $wpdb->show_errors = true; + + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } + + public function test_delete_value() { + add_post_meta( $this->post_id, 'test_single', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertEquals( 'val1', $current ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_single' => null, + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_single', false ); + $this->assertEmpty( $meta ); + } + + /** + * @depends test_delete_value + */ + public function test_delete_value_blocked() { + add_post_meta( $this->post_id, 'test_bad_auth', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_bad_auth', true ); + $this->assertEquals( 'val1', $current ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_bad_auth' => null, + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); + + $meta = get_post_meta( $this->post_id, 'test_bad_auth', true ); + $this->assertEquals( 'val1', $meta ); + } + + /** + * @depends test_delete_value + */ + public function test_delete_value_db_error() { + add_post_meta( $this->post_id, 'test_single', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertEquals( 'val1', $current ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_single' => null, + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + /** + * Disable showing error as the below is going to intentionally + * trigger a DB error. + */ + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_delete_query' ) ); + + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_delete_query' ) ); + $wpdb->show_errors = true; + + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } + + public function test_get_schema() { + $request = new WP_REST_Request( 'OPTIONS', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $response = $this->server->dispatch( $request ); + + $data = $response->get_data(); + $schema = $data['schema']; + + $this->assertArrayHasKey( 'meta', $schema['properties'] ); + $meta_schema = $schema['properties']['meta']['properties']; + + $this->assertArrayHasKey( 'test_single', $meta_schema ); + $this->assertEquals( 'string', $meta_schema['test_single']['type'] ); + + $this->assertArrayHasKey( 'test_multi', $meta_schema ); + $this->assertEquals( 'array', $meta_schema['test_multi']['type'] ); + $this->assertArrayHasKey( 'items', $meta_schema['test_multi'] ); + $this->assertEquals( 'string', $meta_schema['test_multi']['items']['type'] ); + + $this->assertArrayHasKey( 'test_custom_schema', $meta_schema ); + $this->assertEquals( 'number', $meta_schema['test_custom_schema']['type'] ); + + $this->assertArrayNotHasKey( 'test_no_rest', $meta_schema ); + $this->assertArrayNotHasKey( 'test_rest_disabled', $meta_schema ); + $this->assertArrayNotHasKey( 'test_invalid_type', $meta_schema ); + } + + /** + * Internal function used to disable an insert query which + * will trigger a wpdb error for testing purposes. + */ + public function error_insert_query( $query ) { + if ( strpos( $query, 'INSERT' ) === 0 ) { + $query = '],'; + } + return $query; + } + + /** + * Internal function used to disable an insert query which + * will trigger a wpdb error for testing purposes. + */ + public function error_delete_query( $query ) { + if ( strpos( $query, 'DELETE' ) === 0 ) { + $query = '],'; + } + return $query; + } +} From 05f969f468a753ed9e105db467ab1e607ae8f521 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 14:00:09 -0500 Subject: [PATCH 183/318] Attempt to fix update comment content test for PHP 5.2 and 5.3 --- tests/test-rest-comments-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 331e3bcde9..98c3edbf7b 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1061,7 +1061,7 @@ public function test_update_item() { wp_set_current_user( $this->admin_id ); $params = array( - 'content' => "Disco Stu doesn't advertise.", + 'content' => 'Disco Stu doesn\'t advertise.', 'author' => $this->subscriber_id, 'author_name' => 'Disco Stu', 'author_url' => 'http://stusdisco.com', @@ -1080,7 +1080,7 @@ public function test_update_item() { $comment = $response->get_data(); $updated = get_comment( $this->approved_id ); - $this->assertEquals( $params['content'], $comment['content']['raw'] ); + $this->assertEquals( $params['content'], $updated->comment_content ); $this->assertEquals( $params['author'], $comment['author'] ); $this->assertEquals( $params['author_name'], $comment['author_name'] ); $this->assertEquals( $params['author_url'], $comment['author_url'] ); From 548a0dabf954fc98ec9bc7c9fa2b09ce9449a3db Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 14:17:24 -0500 Subject: [PATCH 184/318] Maybe Disco Stu has started self-promoting --- tests/test-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 98c3edbf7b..c111cf68ef 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1061,12 +1061,12 @@ public function test_update_item() { wp_set_current_user( $this->admin_id ); $params = array( - 'content' => 'Disco Stu doesn\'t advertise.', 'author' => $this->subscriber_id, 'author_name' => 'Disco Stu', 'author_url' => 'http://stusdisco.com', 'author_email' => 'stu@stusdisco.com', 'author_ip' => '4.4.4.4', + 'content' => 'Testing.', 'date' => '2014-11-07T10:14:25', 'karma' => 100, 'post' => $post_id, From 42a09043c305b724165e811186a2eb04654668f6 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 6 Oct 2016 15:18:04 -0400 Subject: [PATCH 185/318] Update item schema to include `meta` --- tests/test-rest-attachments-controller.php | 3 ++- tests/test-rest-categories-controller.php | 3 ++- tests/test-rest-comments-controller.php | 3 ++- tests/test-rest-pages-controller.php | 3 ++- tests/test-rest-posts-controller.php | 3 ++- tests/test-rest-tags-controller.php | 3 ++- tests/test-rest-users-controller.php | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/test-rest-attachments-controller.php b/tests/test-rest-attachments-controller.php index 92e2718a03..ceadc0f209 100644 --- a/tests/test-rest-attachments-controller.php +++ b/tests/test-rest-attachments-controller.php @@ -688,7 +688,7 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 22, count( $properties ) ); + $this->assertEquals( 23, count( $properties ) ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'alt_text', $properties ); $this->assertArrayHasKey( 'caption', $properties ); @@ -700,6 +700,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'link', $properties ); $this->assertArrayHasKey( 'media_type', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); $this->assertArrayHasKey( 'mime_type', $properties ); $this->assertArrayHasKey( 'media_details', $properties ); $this->assertArrayHasKey( 'modified', $properties ); diff --git a/tests/test-rest-categories-controller.php b/tests/test-rest-categories-controller.php index 8cc742a3d9..e88a41404d 100644 --- a/tests/test-rest-categories-controller.php +++ b/tests/test-rest-categories-controller.php @@ -767,11 +767,12 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 8, count( $properties ) ); + $this->assertEquals( 9, count( $properties ) ); $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'count', $properties ); $this->assertArrayHasKey( 'description', $properties ); $this->assertArrayHasKey( 'link', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); $this->assertArrayHasKey( 'name', $properties ); $this->assertArrayHasKey( 'parent', $properties ); $this->assertArrayHasKey( 'slug', $properties ); diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 1b3fc0fc2b..0e1b6d9fee 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1322,7 +1322,7 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 17, count( $properties ) ); + $this->assertEquals( 18, count( $properties ) ); $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'author_avatar_urls', $properties ); @@ -1336,6 +1336,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'date_gmt', $properties ); $this->assertArrayHasKey( 'karma', $properties ); $this->assertArrayHasKey( 'link', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); $this->assertArrayHasKey( 'parent', $properties ); $this->assertArrayHasKey( 'post', $properties ); $this->assertArrayHasKey( 'status', $properties ); diff --git a/tests/test-rest-pages-controller.php b/tests/test-rest-pages-controller.php index bd6a73bb84..1b43b6cf74 100644 --- a/tests/test-rest-pages-controller.php +++ b/tests/test-rest-pages-controller.php @@ -359,7 +359,7 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 20, count( $properties ) ); + $this->assertEquals( 21, count( $properties ) ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'comment_status', $properties ); $this->assertArrayHasKey( 'content', $properties ); @@ -371,6 +371,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'link', $properties ); $this->assertArrayHasKey( 'menu_order', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); $this->assertArrayHasKey( 'modified', $properties ); $this->assertArrayHasKey( 'modified_gmt', $properties ); $this->assertArrayHasKey( 'parent', $properties ); diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index ea5b8bb11a..4f9871f646 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -1752,7 +1752,7 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 22, count( $properties ) ); + $this->assertEquals( 23, count( $properties ) ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'comment_status', $properties ); $this->assertArrayHasKey( 'content', $properties ); @@ -1764,6 +1764,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'format', $properties ); $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'link', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); $this->assertArrayHasKey( 'modified', $properties ); $this->assertArrayHasKey( 'modified_gmt', $properties ); $this->assertArrayHasKey( 'password', $properties ); diff --git a/tests/test-rest-tags-controller.php b/tests/test-rest-tags-controller.php index 6b7f663192..bd07114b87 100644 --- a/tests/test-rest-tags-controller.php +++ b/tests/test-rest-tags-controller.php @@ -583,11 +583,12 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 7, count( $properties ) ); + $this->assertEquals( 8, count( $properties ) ); $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'count', $properties ); $this->assertArrayHasKey( 'description', $properties ); $this->assertArrayHasKey( 'link', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); $this->assertArrayHasKey( 'name', $properties ); $this->assertArrayHasKey( 'slug', $properties ); $this->assertArrayHasKey( 'taxonomy', $properties ); diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 0619b0938f..00657c9338 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -1105,7 +1105,7 @@ public function test_get_item_schema() { $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 17, count( $properties ) ); + $this->assertEquals( 18, count( $properties ) ); $this->assertArrayHasKey( 'avatar_urls', $properties ); $this->assertArrayHasKey( 'capabilities', $properties ); $this->assertArrayHasKey( 'description', $properties ); @@ -1115,6 +1115,7 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'last_name', $properties ); $this->assertArrayHasKey( 'link', $properties ); + $this->assertArrayHasKey( 'meta', $properties ); $this->assertArrayHasKey( 'name', $properties ); $this->assertArrayHasKey( 'nickname', $properties ); $this->assertArrayHasKey( 'registered_date', $properties ); From 506d1167b77b2bef834a5ebf656a3e9b3a54ac60 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 14:42:57 -0500 Subject: [PATCH 186/318] I hate PHP 5.3 and lower --- tests/test-rest-comments-controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index c111cf68ef..711efe5d8a 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1080,7 +1080,6 @@ public function test_update_item() { $comment = $response->get_data(); $updated = get_comment( $this->approved_id ); - $this->assertEquals( $params['content'], $updated->comment_content ); $this->assertEquals( $params['author'], $comment['author'] ); $this->assertEquals( $params['author_name'], $comment['author_name'] ); $this->assertEquals( $params['author_url'], $comment['author_url'] ); From 8473bedd24e0a00b8d7375308070550725f47fc3 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 6 Oct 2016 14:58:45 -0500 Subject: [PATCH 187/318] Correct sanitization kses functions and re-add failing update content test --- lib/endpoints/class-wp-rest-comments-controller.php | 4 ++-- tests/test-rest-comments-controller.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index d30dc22609..c067a27996 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -753,9 +753,9 @@ protected function prepare_item_for_database( $request ) { $prepared_comment['comment_content'] = $request['content']; } if ( current_user_can( 'unfiltered_html' ) && isset( $prepared_comment['comment_content'] ) ) { - $prepared_comment['comment_content'] = wp_filter_post_kses( $prepared_comment['comment_content'] ); + $prepared_comment['comment_content'] = wp_kses_post( $prepared_comment['comment_content'] ); } elseif ( isset( $prepared_comment['comment_content'] ) ) { - $prepared_comment['comment_content'] = wp_filter_kses( $prepared_comment['comment_content'] ); + $prepared_comment['comment_content'] = wp_kses( $prepared_comment['comment_content'] ); } if ( isset( $request['post'] ) ) { diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 711efe5d8a..b7c4cb9207 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1080,6 +1080,7 @@ public function test_update_item() { $comment = $response->get_data(); $updated = get_comment( $this->approved_id ); + $this->assertEquals( $params['content'], $comment['content']['raw'] ); $this->assertEquals( $params['author'], $comment['author'] ); $this->assertEquals( $params['author_name'], $comment['author_name'] ); $this->assertEquals( $params['author_url'], $comment['author_url'] ); From eb73e622ddd1f08adce1996293dd295838d2c43d Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 6 Oct 2016 17:49:45 -0400 Subject: [PATCH 188/318] Handle multiple duplicate values correctly --- lib/fields/class-wp-rest-meta-fields.php | 31 +++++++++++++++++++----- tests/test-rest-post-meta-fields.php | 28 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index eabf5bef70..ab674a42e3 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -162,11 +162,30 @@ protected function update_multi_meta_value( $object, $name, $values ) { } $current = get_metadata( $this->get_meta_type(), $object, $name, false ); - $to_add = array_diff( $values, $current ); - $to_remove = array_diff( $current, $values ); - foreach ( $to_add as $value ) { - if ( ! add_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { + $to_remove = $current; + $to_add = $values; + foreach ( $to_add as $add_key => $value ) { + $remove_keys = array_keys( $to_remove, $value, true ); + if ( empty( $remove_keys ) ) { + continue; + } + + if ( count( $remove_keys ) > 1 ) { + // To remove, we need to remove first, then add, so don't touch. + continue; + } + + $remove_key = $remove_keys[0]; + unset( $to_remove[ $remove_key ] ); + unset( $to_add[ $add_key ] ); + } + + // `delete_metadata` removes _all_ instances of the value, so only call + // once. + $to_remove = array_unique( $to_remove ); + foreach ( $to_remove as $value ) { + if ( ! delete_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), @@ -174,8 +193,8 @@ protected function update_multi_meta_value( $object, $name, $values ) { ); } } - foreach ( $to_remove as $value ) { - if ( ! delete_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { + foreach ( $to_add as $value ) { + if ( ! add_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), diff --git a/tests/test-rest-post-meta-fields.php b/tests/test-rest-post-meta-fields.php index 9de32dfb0f..b4c3472914 100644 --- a/tests/test-rest-post-meta-fields.php +++ b/tests/test-rest-post-meta-fields.php @@ -340,6 +340,34 @@ public function test_set_value_multiple() { $this->assertContains( 'val2', $meta ); } + /** + * Test removing only one item with duplicate items. + */ + public function test_set_value_remove_one() { + add_post_meta( $this->post_id, 'test_multi', 'c' ); + add_post_meta( $this->post_id, 'test_multi', 'n' ); + add_post_meta( $this->post_id, 'test_multi', 'n' ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_multi' => array( 'c', 'n' ), + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 2, $meta ); + $this->assertContains( 'c', $meta ); + $this->assertContains( 'n', $meta ); + } + /** * @depends test_set_value_multiple */ From aab6377e2e646139b6e026d791e4c688dc98c4f1 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 6 Oct 2016 18:07:19 -0400 Subject: [PATCH 189/318] Exclude unsupported query parameters --- .../class-wp-rest-posts-controller.php | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 6759065751..da948bb441 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -94,22 +94,32 @@ public function get_items( $request ) { return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) ); } - $args = array(); - $args['author__in'] = $request['author']; - $args['author__not_in'] = $request['author_exclude']; - $args['menu_order'] = $request['menu_order']; - $args['offset'] = $request['offset']; - $args['order'] = $request['order']; - $args['orderby'] = $request['orderby']; - $args['paged'] = $request['page']; - $args['post__in'] = $request['include']; - $args['post__not_in'] = $request['exclude']; - $args['posts_per_page'] = $request['per_page']; - $args['name'] = $request['slug']; - $args['post_parent__in'] = $request['parent']; - $args['post_parent__not_in'] = $request['parent_exclude']; - $args['post_status'] = $request['status']; - $args['s'] = $request['search']; + $registered = $this->get_collection_params(); + $args = array(); + $args['offset'] = $request['offset']; + $args['order'] = $request['order']; + $args['orderby'] = $request['orderby']; + $args['paged'] = $request['page']; + $args['post__in'] = $request['include']; + $args['post__not_in'] = $request['exclude']; + $args['posts_per_page'] = $request['per_page']; + $args['name'] = $request['slug']; + $args['post_status'] = $request['status']; + $args['s'] = $request['search']; + + if ( isset( $registered['author'] ) ) { + $args['author__in'] = $request['author']; + $args['author__not_in'] = $request['author_exclude']; + } + + if ( isset( $registered['menu_order'] ) ) { + $args['menu_order'] = $request['menu_order']; + } + + if ( isset( $registered['parent'] ) ) { + $args['post_parent__in'] = $request['parent']; + $args['post_parent__not_in'] = $request['parent_exclude']; + } $args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. @@ -127,7 +137,7 @@ public function get_items( $request ) { unset( $args['filter'] ); } - if ( isset( $request['sticky'] ) ) { + if ( isset( $registered['sticky'] ) && isset( $request['sticky'] ) ) { $sticky_posts = get_option( 'sticky_posts', array() ); if ( $sticky_posts && $request['sticky'] ) { // As post__in will be used to only get sticky posts, From 62a75b92fa682425f7f363867528663d9bfe67b3 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Thu, 6 Oct 2016 18:44:44 -0400 Subject: [PATCH 190/318] Check other query parameters against registered --- .../class-wp-rest-posts-controller.php | 60 +++++++++++++------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index e415c225fd..613c2e4b51 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -94,50 +94,72 @@ public function get_items( $request ) { return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) ); } - $registered = $this->get_collection_params(); - $args = array(); - $args['offset'] = $request['offset']; - $args['order'] = $request['order']; - $args['orderby'] = $request['orderby']; - $args['paged'] = $request['page']; - $args['post__in'] = $request['include']; - $args['post__not_in'] = $request['exclude']; - $args['name'] = $request['slug']; - $args['post_status'] = $request['status']; - $args['s'] = $request['search']; + $registered = $this->get_collection_params(); + $args = array(); + if ( isset( $registered['offset'] ) ) { + $args['offset'] = $request['offset']; + } + if ( isset( $registered['order'] ) ) { + $args['order'] = $request['order']; + } + if ( isset( $registered['orderby'] ) ) { + $args['orderby'] = $request['orderby']; + } + if ( isset( $registered['page'] ) ) { + $args['paged'] = $request['page']; + } + if ( isset( $registered['include'] ) ) { + $args['post__in'] = $request['include']; + } + if ( isset( $registered['exclude'] ) ) { + $args['post__not_in'] = $request['exclude']; + } + if ( isset( $registered['slug'] ) ) { + $args['name'] = $request['slug']; + } + if ( isset( $registered['status'] ) ) { + $args['post_status'] = $request['status']; + } + if ( isset( $registered['search'] ) ) { + $args['s'] = $request['search']; + } if ( isset( $registered['author'] ) ) { - $args['author__in'] = $request['author']; + $args['author__in'] = $request['author']; + } + if ( isset( $registered['author_exclude'] ) ) { $args['author__not_in'] = $request['author_exclude']; } - if ( isset( $registered['menu_order'] ) ) { $args['menu_order'] = $request['menu_order']; } - if ( isset( $registered['parent'] ) ) { - $args['post_parent__in'] = $request['parent']; + $args['post_parent__in'] = $request['parent']; + } + if ( isset( $registered['parent_exclude'] ) ) { $args['post_parent__not_in'] = $request['parent_exclude']; } $args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. - if ( isset( $request['before'] ) ) { + if ( isset( $registered['before'] && isset( $request['before'] ) ) { $args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. - if ( isset( $request['after'] ) ) { + if ( isset( $registered['after'] && isset( $request['after'] ) ) { $args['date_query'][0]['after'] = $request['after']; } - if ( is_array( $request['filter'] ) ) { + if ( isset( $registered['filter'] && is_array( $request['filter'] ) ) { $args = array_merge( $args, $request['filter'] ); unset( $args['filter'] ); } // Ensure our per_page parameter overrides filter. - $args['posts_per_page'] = $request['per_page']; + if ( isset( $registered['per_page'] ) { + $args['posts_per_page'] = $request['per_page']; + } if ( isset( $registered['sticky'] ) && isset( $request['sticky'] ) ) { $sticky_posts = get_option( 'sticky_posts', array() ); From d474fe0be60a31145105bf1f8e2d3798b5953f7d Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Fri, 7 Oct 2016 08:22:15 -0500 Subject: [PATCH 191/318] Switch to `wp_filter_kses()` in an attempt to avoid having to pass global allowed tags --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index c067a27996..c802630bda 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -755,7 +755,7 @@ protected function prepare_item_for_database( $request ) { if ( current_user_can( 'unfiltered_html' ) && isset( $prepared_comment['comment_content'] ) ) { $prepared_comment['comment_content'] = wp_kses_post( $prepared_comment['comment_content'] ); } elseif ( isset( $prepared_comment['comment_content'] ) ) { - $prepared_comment['comment_content'] = wp_kses( $prepared_comment['comment_content'] ); + $prepared_comment['comment_content'] = wp_filter_kses( $prepared_comment['comment_content'] ); } if ( isset( $request['post'] ) ) { From a32410ad517c398aff9b97cc08dfd7a50bfe6b01 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Fri, 7 Oct 2016 08:54:09 -0500 Subject: [PATCH 192/318] Pass current filter into wp_kses --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index c802630bda..ab82dd718e 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -755,7 +755,7 @@ protected function prepare_item_for_database( $request ) { if ( current_user_can( 'unfiltered_html' ) && isset( $prepared_comment['comment_content'] ) ) { $prepared_comment['comment_content'] = wp_kses_post( $prepared_comment['comment_content'] ); } elseif ( isset( $prepared_comment['comment_content'] ) ) { - $prepared_comment['comment_content'] = wp_filter_kses( $prepared_comment['comment_content'] ); + $prepared_comment['comment_content'] = wp_kses( $prepared_comment['comment_content'], 'pre_comment_content' ); } if ( isset( $request['post'] ) ) { From d7909ebf5c2793aecaf8f3405ce7021cb4c5df29 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Fri, 7 Oct 2016 09:34:20 -0500 Subject: [PATCH 193/318] Add back string type checking to content values --- lib/endpoints/class-wp-rest-comments-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index ab82dd718e..5b23f0e506 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -747,10 +747,10 @@ protected function prepare_item_for_database( $request ) { * Allow the comment_content to be set via the 'content' or * the 'content.raw' properties of the Request object. */ - if ( isset( $request['content']['raw'] ) ) { - $prepared_comment['comment_content'] = $request['content']['raw']; - } elseif ( isset( $request['content'] ) ) { + if ( isset( $request['content'] ) && is_string( $request['content'] ) ) { $prepared_comment['comment_content'] = $request['content']; + } elseif ( isset( $request['content']['raw'] ) && is_string( $request['content']['raw'] ) ) { + $prepared_comment['comment_content'] = $request['content']['raw']; } if ( current_user_can( 'unfiltered_html' ) && isset( $prepared_comment['comment_content'] ) ) { $prepared_comment['comment_content'] = wp_kses_post( $prepared_comment['comment_content'] ); From 6e1f9bac142d8c8dd4293466f47b3d042b190307 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Fri, 7 Oct 2016 09:37:07 -0500 Subject: [PATCH 194/318] Test theory that the problem is caused by and admin updating a comment as a different user --- tests/test-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index b7c4cb9207..e74a569ed3 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1061,7 +1061,7 @@ public function test_update_item() { wp_set_current_user( $this->admin_id ); $params = array( - 'author' => $this->subscriber_id, + 'author' => $this->admin_id, 'author_name' => 'Disco Stu', 'author_url' => 'http://stusdisco.com', 'author_email' => 'stu@stusdisco.com', From dc696383f5efab39f26f4fa1bd9576e848269d10 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Fri, 7 Oct 2016 21:55:46 -0500 Subject: [PATCH 195/318] Bump plugin version to 2.0beta15. One more beta release and the plugin can drive! --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index 7968ddb7fe..2aa9b62763 100755 --- a/plugin.php +++ b/plugin.php @@ -4,7 +4,7 @@ * Description: JSON-based REST API for WordPress, originally developed as part of GSoC 2013. * Author: WP REST API Team * Author URI: http://v2.wp-api.org - * Version: 2.0-beta14 + * Version: 2.0-beta15 * Plugin URI: https://github.com/WP-API/WP-API * License: GPL2+ */ From b4706fc4db6803987a2ccd44b6f3c18cd45447af Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Fri, 7 Oct 2016 21:56:15 -0500 Subject: [PATCH 196/318] Update changelog with changes since beta14 --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd7020388b..b2481ba8b1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,59 @@ # Changelog +## 2.0 Beta 15.0 (October 07, 2016) + +- Introduce support for Post Meta, Term Meta, User Meta, and Comment Meta in +their parent endpoints. + + For your meta fields to be exposed in the REST API, you need to register + them. WordPress includes a `register_meta()` function which is not usually + required to get/set fields, but is required for API support. + + To register your field, simply call register_meta and set the show_in_rest + flag to true. Note: register_meta must be called separately for each meta + key. + + (props @rmccue, @danielbachhuber, @kjbenk, @duncanjbrown, [#2765][gh-2765]) + +- Introduce Settings endpoint. + + Expose options to the REST API with the `register_setting()` function, by + passing `$args = array( 'show_in_rest' => true )`. Note: WordPress 4.7 is + required. See changeset [38635][https://core.trac.wordpress.org/changeset/38635]. + + (props @joehoyle, @fjarrett, @danielbachhuber, @jonathanbardo, + @greatislander, [#2739][gh-2739]) + +- Attachments controller, change permissions check to match core. + + Check for the `upload_files` capability when creating an attachment. + + (props @nullvariable, @adamsilverstein, [#2743][gh-2743]) + +- Add `?{taxonomy}_exclude=` query parameter + + This mirrors our existing support for ?{taxonomy}= filtering in the posts + controller (which allows querying for only records with are associated with + any of the provided term IDs for the specified taxonomy) by adding an + equivalent `_exclude` variant to list IDs of terms for which associated posts + should NOT be returned. + + (props @kadamwhite, [#2756][gh-2756]) + +- Use `get_comment_type()` when comparing updating comment status. + + Comments having a empty `comment_type` within WordPress bites us again. + Fixes a bug where comments could not be updated because of bad comparison + logic. + + (props @joehoyle, [#2753][gh-2753]) + +[gh-2765]: https://github.com/WP-API/WP-API/issues/2765 +[gh-2739]: https://github.com/WP-API/WP-API/issues/2739 +[gh-2743]: https://github.com/WP-API/WP-API/issues/2743 +[gh-2756]: https://github.com/WP-API/WP-API/issues/2756 +[gh-2753]: https://github.com/WP-API/WP-API/issues/2753 + ## 2.0 Beta 14.0 (September 30, 2016) - Add support for password protected posts From 61c6872e22251a36179716f6b1308090ee8208cb Mon Sep 17 00:00:00 2001 From: Boone B Gorges Date: Sat, 8 Oct 2016 21:29:37 -0500 Subject: [PATCH 197/318] Documentation and code standards for `WP_REST_Terms_Controller`. --- .../class-wp-rest-terms-controller.php | 229 +++++++++--------- 1 file changed, 121 insertions(+), 108 deletions(-) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index f963b5a0fc..a062e1e757 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -1,14 +1,22 @@ taxonomy = $taxonomy; @@ -20,7 +28,7 @@ public function __construct( $taxonomy ) { } /** - * Register the routes for the objects of the controller. + * Registers the routes for the objects of the controller. */ public function register_routes() { @@ -32,34 +40,34 @@ public function register_routes() { 'args' => $this->get_collection_params(), ), array( - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'create_item' ), + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'create_item' ), 'permission_callback' => array( $this, 'create_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), ), 'schema' => array( $this, 'get_public_item_schema' ), - )); + ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ), 'args' => array( - 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), ), ), array( - 'methods' => WP_REST_Server::EDITABLE, - 'callback' => array( $this, 'update_item' ), + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'update_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), array( - 'methods' => WP_REST_Server::DELETABLE, - 'callback' => array( $this, 'delete_item' ), + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_item' ), 'permission_callback' => array( $this, 'delete_item_permissions_check' ), - 'args' => array( - 'force' => array( + 'args' => array( + 'force' => array( 'default' => false, 'description' => __( 'Required to be true, as resource does not support trashing.' ), ), @@ -70,7 +78,7 @@ public function register_routes() { } /** - * Check if a given request has access to read the terms. + * Checks if a request has access to read terms in the specified taxonomy. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean @@ -87,9 +95,9 @@ public function get_items_permissions_check( $request ) { } /** - * Get terms associated with a taxonomy + * Gets terms associated with a taxonomy. * - * @param WP_REST_Request $request Full details about the request + * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error */ public function get_items( $request ) { @@ -108,7 +116,7 @@ public function get_items( $request ) { if ( ! empty( $request['offset'] ) ) { $prepared_args['offset'] = $request['offset']; } else { - $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; + $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; } $taxonomy_obj = get_taxonomy( $this->taxonomy ); @@ -125,7 +133,7 @@ public function get_items( $request ) { } /** - * Filter the query arguments, before passing them to `get_terms()`. + * Filters the query arguments before passing them to get_terms(). * * Enables adding extra arguments or setting defaults for a terms * collection request. @@ -133,7 +141,7 @@ public function get_items( $request ) { * @see https://developer.wordpress.org/reference/functions/get_terms/ * * @param array $prepared_args Array of arguments to be - * passed to get_terms. + * passed to get_terms(). * @param WP_REST_Request $request The current request. */ $prepared_args = apply_filters( "rest_{$this->taxonomy}_query", $prepared_args, $request ); @@ -176,7 +184,7 @@ public function get_items( $request ) { $response = rest_ensure_response( $response ); - // Store pagation values for headers then unset for count query. + // Store pagination values for headers. $per_page = (int) $prepared_args['number']; $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); @@ -203,14 +211,14 @@ public function get_items( $request ) { } /** - * Get the terms attached to a post. + * Gets the terms attached to a post. * - * This is an alternative to `get_terms()` that uses `get_the_terms()` + * This is an alternative to get_terms() that uses get_the_terms() * instead, which hits the object cache. There are a few things not * supported, notably `include`, `exclude`. In `self::get_items()` these * are instead treated as a full query. * - * @param array $prepared_args Arguments for `get_terms()` + * @param array $prepared_args Arguments for get_terms(). * @return array List of term objects. (Total count in `$this->total_terms`) */ protected function get_terms_for_post( $prepared_args ) { @@ -220,8 +228,10 @@ protected function get_terms_for_post( $prepared_args ) { return array(); } - // get_items() verifies that we don't have `include` set, and default - // ordering is by `name` + /* + * get_items() verifies that we don't have `include` set, and default + * ordering is by `name`. + */ if ( ! in_array( $prepared_args['orderby'], array( 'name', 'none', 'include' ) ) ) { switch ( $prepared_args['orderby'] ) { case 'id': @@ -241,7 +251,7 @@ protected function get_terms_for_post( $prepared_args ) { $query_result = array_reverse( $query_result ); } - // Pagination + // Pagination. $this->total_terms = count( $query_result ); $query_result = array_slice( $query_result, $prepared_args['offset'], $prepared_args['number'] ); @@ -253,6 +263,8 @@ protected function get_terms_for_post( $prepared_args ) { * * Uses `$this->sort_column` to determine field to sort by. * + * @access protected + * * @param stdClass $left Term object. * @param stdClass $right Term object. * @return int <0 if left is higher "priority" than right, 0 if equal, >0 if right is higher "priority" than left. @@ -270,7 +282,7 @@ protected function compare_terms( $left, $right ) { } /** - * Check if a given request has access to read a term. + * Checks if a request has access to read the specified term. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean @@ -287,7 +299,7 @@ public function get_item_permissions_check( $request ) { } /** - * Get a single term from a taxonomy + * Gets a single term from a taxonomy. * * @param WP_REST_Request $request Full details about the request * @return WP_REST_Request|WP_Error @@ -308,7 +320,7 @@ public function get_item( $request ) { } /** - * Check if a given request has access to create a term + * Checks if a request has access to create a term. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean @@ -328,7 +340,7 @@ public function create_item_permissions_check( $request ) { } /** - * Create a single term for a taxonomy + * Creates a single term in a taxonomy. * * @param WP_REST_Request $request Full details about the request * @return WP_REST_Request|WP_Error @@ -351,9 +363,10 @@ public function create_item( $request ) { $term = wp_insert_term( $prepared_term->name, $this->taxonomy, $prepared_term ); if ( is_wp_error( $term ) ) { - // If we're going to inform the client that the term exists, give them the identifier - // they can actually use. - + /* + * If we're going to inform the client that the term already exists, + * give them the identifier for future use. + */ if ( ( $term_id = $term->get_error_data( 'term_exists' ) ) ) { $existing_term = get_term( $term_id, $this->taxonomy ); $term->add_data( $existing_term->term_id, 'term_exists' ); @@ -368,8 +381,8 @@ public function create_item( $request ) { * Fires after a single term is created or updated via the REST API. * * @param WP_Term $term Inserted Term object. - * @param WP_REST_Request $request Request object. - * @param boolean $creating True when creating term, false when updating. + * @param WP_REST_Request $request Request object. + * @param boolean $creating True when creating term, false when updating. */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, true ); @@ -394,7 +407,7 @@ public function create_item( $request ) { } /** - * Check if a given request has access to update a term + * Checks if a request has access to update the specified term. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean @@ -419,7 +432,7 @@ public function update_item_permissions_check( $request ) { } /** - * Update a single term from a taxonomy + * Updates a single term from a taxonomy. * * @param WP_REST_Request $request Full details about the request * @return WP_REST_Request|WP_Error @@ -473,7 +486,7 @@ public function update_item( $request ) { } /** - * Check if a given request has access to delete a term + * Checks if a request has access to delete the specified term. * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean @@ -494,7 +507,7 @@ public function delete_item_permissions_check( $request ) { } /** - * Delete a single term from a taxonomy + * Deletes a single term from a taxonomy. * * @param WP_REST_Request $request Full details about the request * @return WP_REST_Response|WP_Error @@ -503,7 +516,7 @@ public function delete_item( $request ) { $force = isset( $request['force'] ) ? (bool) $request['force'] : false; - // We don't support trashing for this type, error out + // We don't support trashing for this resource type. if ( ! $force ) { return new WP_Error( 'rest_trash_not_supported', __( 'Resource does not support trashing.' ), array( 'status' => 501 ) ); } @@ -530,7 +543,7 @@ public function delete_item( $request ) { } /** - * Prepare a single term for create or update + * Prepares a single term for create or update. * * @param WP_REST_Request $request Request object. * @return object $prepared_term Term object. @@ -567,7 +580,7 @@ public function prepare_item_for_database( $request ) { } /** - * Filter term data before inserting term via the REST API. + * Filters term data before inserting term via the REST API. * * @param object $prepared_term Term object. * @param WP_REST_Request $request Request object. @@ -576,10 +589,10 @@ public function prepare_item_for_database( $request ) { } /** - * Prepare a single term output for response + * Prepares a single term output for response. * - * @param obj $item Term object - * @param WP_REST_Request $request + * @param obj $item Term object. + * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response */ public function prepare_item_for_response( $item, $request ) { @@ -623,7 +636,7 @@ public function prepare_item_for_response( $item, $request ) { $response->add_links( $this->prepare_links( $item ) ); /** - * Filter a term item returned from the API. + * Filters a term item returned from the API. * * Allows modification of the term data right before it is returned. * @@ -635,7 +648,7 @@ public function prepare_item_for_response( $item, $request ) { } /** - * Prepare links for the request. + * Prepares links for the request. * * @param object $term Term object. * @return array Links for the given term. @@ -644,13 +657,13 @@ protected function prepare_links( $term ) { $base = $this->namespace . '/' . $this->rest_base; $links = array( 'self' => array( - 'href' => rest_url( trailingslashit( $base ) . $term->term_id ), + 'href' => rest_url( trailingslashit( $base ) . $term->term_id ), ), 'collection' => array( - 'href' => rest_url( $base ), + 'href' => rest_url( $base ), ), 'about' => array( - 'href' => rest_url( sprintf( 'wp/v2/taxonomies/%s', $this->taxonomy ) ), + 'href' => rest_url( sprintf( 'wp/v2/taxonomies/%s', $this->taxonomy ) ), ), ); @@ -688,29 +701,29 @@ protected function prepare_links( $term ) { } /** - * Get the Term's schema, conforming to JSON Schema + * Gets the term's schema, conforming to JSON Schema. * * @return array */ public function get_item_schema() { $schema = array( - '$schema' => 'http://json-schema.org/draft-04/schema#', - 'title' => 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy, - 'type' => 'object', - 'properties' => array( - 'id' => array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy, + 'type' => 'object', + 'properties' => array( + 'id' => array( 'description' => __( 'Unique identifier for the resource.' ), 'type' => 'integer', 'context' => array( 'view', 'embed', 'edit' ), 'readonly' => true, ), - 'count' => array( + 'count' => array( 'description' => __( 'Number of published posts for the resource.' ), 'type' => 'integer', 'context' => array( 'view', 'edit' ), 'readonly' => true, ), - 'description' => array( + 'description' => array( 'description' => __( 'HTML description of the resource.' ), 'type' => 'string', 'context' => array( 'view', 'edit' ), @@ -718,14 +731,14 @@ public function get_item_schema() { 'sanitize_callback' => 'wp_filter_post_kses', ), ), - 'link' => array( + 'link' => array( 'description' => __( 'URL to the resource.' ), 'type' => 'string', 'format' => 'uri', 'context' => array( 'view', 'embed', 'edit' ), 'readonly' => true, ), - 'name' => array( + 'name' => array( 'description' => __( 'HTML title for the resource.' ), 'type' => 'string', 'context' => array( 'view', 'embed', 'edit' ), @@ -734,7 +747,7 @@ public function get_item_schema() { ), 'required' => true, ), - 'slug' => array( + 'slug' => array( 'description' => __( 'An alphanumeric identifier for the resource unique to its type.' ), 'type' => 'string', 'context' => array( 'view', 'embed', 'edit' ), @@ -742,7 +755,7 @@ public function get_item_schema() { 'sanitize_callback' => array( $this, 'sanitize_slug' ), ), ), - 'taxonomy' => array( + 'taxonomy' => array( 'description' => __( 'Type attribution for the resource.' ), 'type' => 'string', 'enum' => array_keys( get_taxonomies() ), @@ -765,7 +778,7 @@ public function get_item_schema() { } /** - * Get the query params for collections + * Gets the query params for collections. * * @return array */ @@ -776,42 +789,42 @@ public function get_collection_params() { $query_params['context']['default'] = 'view'; $query_params['exclude'] = array( - 'description' => __( 'Ensure result set excludes specific ids.' ), - 'type' => 'array', - 'default' => array(), - 'sanitize_callback' => 'wp_parse_id_list', + 'description' => __( 'Ensure result set excludes specific ids.' ), + 'type' => 'array', + 'default' => array(), + 'sanitize_callback' => 'wp_parse_id_list', ); $query_params['include'] = array( - 'description' => __( 'Limit result set to specific ids.' ), - 'type' => 'array', - 'default' => array(), - 'sanitize_callback' => 'wp_parse_id_list', + 'description' => __( 'Limit result set to specific ids.' ), + 'type' => 'array', + 'default' => array(), + 'sanitize_callback' => 'wp_parse_id_list', ); if ( ! $taxonomy->hierarchical ) { $query_params['offset'] = array( - 'description' => __( 'Offset the result set by a specific number of items.' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', + 'description' => __( 'Offset the result set by a specific number of items.' ), + 'type' => 'integer', + 'sanitize_callback' => 'absint', + 'validate_callback' => 'rest_validate_request_arg', ); } - $query_params['order'] = array( - 'description' => __( 'Order sort attribute ascending or descending.' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_key', - 'default' => 'asc', - 'enum' => array( + $query_params['order'] = array( + 'description' => __( 'Order sort attribute ascending or descending.' ), + 'type' => 'string', + 'sanitize_callback' => 'sanitize_key', + 'default' => 'asc', + 'enum' => array( 'asc', 'desc', ), - 'validate_callback' => 'rest_validate_request_arg', + 'validate_callback' => 'rest_validate_request_arg', ); - $query_params['orderby'] = array( - 'description' => __( 'Sort collection by resource attribute.' ), - 'type' => 'string', - 'sanitize_callback' => 'sanitize_key', - 'default' => 'name', - 'enum' => array( + $query_params['orderby'] = array( + 'description' => __( 'Sort collection by resource attribute.' ), + 'type' => 'string', + 'sanitize_callback' => 'sanitize_key', + 'default' => 'name', + 'enum' => array( 'id', 'include', 'name', @@ -820,38 +833,38 @@ public function get_collection_params() { 'description', 'count', ), - 'validate_callback' => 'rest_validate_request_arg', + 'validate_callback' => 'rest_validate_request_arg', ); $query_params['hide_empty'] = array( - 'description' => __( 'Whether to hide resources not assigned to any posts.' ), - 'type' => 'boolean', - 'default' => false, - 'validate_callback' => 'rest_validate_request_arg', + 'description' => __( 'Whether to hide resources not assigned to any posts.' ), + 'type' => 'boolean', + 'default' => false, + 'validate_callback' => 'rest_validate_request_arg', ); if ( $taxonomy->hierarchical ) { $query_params['parent'] = array( - 'description' => __( 'Limit result set to resources assigned to a specific parent.' ), - 'type' => 'integer', - 'sanitize_callback' => 'absint', - 'validate_callback' => 'rest_validate_request_arg', + 'description' => __( 'Limit result set to resources assigned to a specific parent.' ), + 'type' => 'integer', + 'sanitize_callback' => 'absint', + 'validate_callback' => 'rest_validate_request_arg', ); } $query_params['post'] = array( - 'description' => __( 'Limit result set to resources assigned to a specific post.' ), - 'type' => 'integer', - 'default' => null, - 'validate_callback' => 'rest_validate_request_arg', + 'description' => __( 'Limit result set to resources assigned to a specific post.' ), + 'type' => 'integer', + 'default' => null, + 'validate_callback' => 'rest_validate_request_arg', ); - $query_params['slug'] = array( - 'description' => __( 'Limit result set to resources with a specific slug.' ), - 'type' => 'string', - 'validate_callback' => 'rest_validate_request_arg', + $query_params['slug'] = array( + 'description' => __( 'Limit result set to resources with a specific slug.' ), + 'type' => 'string', + 'validate_callback' => 'rest_validate_request_arg', ); return $query_params; } /** - * Check that the taxonomy is valid + * Checks that the taxonomy is valid. * * @param string * @return WP_Error|boolean From 2e8047a7b5b84c3ed217eab710f32cc546405b50 Mon Sep 17 00:00:00 2001 From: Boone B Gorges Date: Sat, 8 Oct 2016 21:35:39 -0500 Subject: [PATCH 198/318] Remove temporary fix for upstream bug 35614. PR #2313 included a fix for a core bug that caused `get_terms()` to return results when the `offset` parameter was greater than the total number of terms available. This bug was fixed in WP 4.5 and the workaround in WP-API is no longer needed. --- lib/endpoints/class-wp-rest-terms-controller.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index f963b5a0fc..74a62a2118 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -157,12 +157,6 @@ public function get_items( $request ) { unset( $count_args['offset'] ); $total_terms = wp_count_terms( $this->taxonomy, $count_args ); - // Ensure we don't return results when offset is out of bounds - // see https://core.trac.wordpress.org/ticket/35935 - if ( $prepared_args['offset'] >= $total_terms ) { - $query_result = array(); - } - // wp_count_terms can return a falsy value when the term has no children if ( ! $total_terms ) { $total_terms = 0; From cc55f58358aa5463911a2a3f665989981693e144 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Mon, 10 Oct 2016 11:12:31 -0500 Subject: [PATCH 199/318] Remove conditional check on the `unfiltered_html` cap for discussion Use the `wp_filter_kses` sanitization function for all new comments --- lib/endpoints/class-wp-rest-comments-controller.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index e8d90c8b7a..d456bfcc67 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -774,14 +774,9 @@ protected function prepare_item_for_database( $request ) { * the 'content.raw' properties of the Request object. */ if ( isset( $request['content'] ) && is_string( $request['content'] ) ) { - $prepared_comment['comment_content'] = $request['content']; + $prepared_comment['comment_content'] = wp_filter_kses( $request['content'] ); } elseif ( isset( $request['content']['raw'] ) && is_string( $request['content']['raw'] ) ) { - $prepared_comment['comment_content'] = $request['content']['raw']; - } - if ( current_user_can( 'unfiltered_html' ) && isset( $prepared_comment['comment_content'] ) ) { - $prepared_comment['comment_content'] = wp_kses_post( $prepared_comment['comment_content'] ); - } elseif ( isset( $prepared_comment['comment_content'] ) ) { - $prepared_comment['comment_content'] = wp_kses( $prepared_comment['comment_content'], 'pre_comment_content' ); + $prepared_comment['comment_content'] = wp_filter_kses( $request['content']['raw'] ); } if ( isset( $request['post'] ) ) { From 1496b39fbf1928e6cbcd854be39268d7609a079e Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Mon, 10 Oct 2016 11:13:52 -0500 Subject: [PATCH 200/318] Revert 6e1f9bac142d8c8dd4293466f47b3d042b190307 This was just a test for debugging why unit tests where failing --- tests/test-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index e6b62f7b29..27d276b1f8 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1191,7 +1191,7 @@ public function test_update_item() { wp_set_current_user( $this->admin_id ); $params = array( - 'author' => $this->admin_id, + 'author' => $this->subscriber_id, 'author_name' => 'Disco Stu', 'author_url' => 'http://stusdisco.com', 'author_email' => 'stu@stusdisco.com', From c47d30ea28bc1551d1cac0b4367c4a19f07ca26f Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Mon, 10 Oct 2016 14:17:55 -0500 Subject: [PATCH 201/318] Correct @see url for WP_Query --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 3cca9801a8..830dfc68b7 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -162,7 +162,7 @@ public function get_items( $request ) { * Enables adding extra arguments or setting defaults for a post * collection request. * - * @see https://developer.wordpress.org/reference/classes/wp_user_query/ + * @see https://developer.wordpress.org/reference/classes/wp_query/ * * @param array $args Key value array of query var to query value. * @param WP_REST_Request $request The request used. From b6d986fcce6a2c16ac83beee72ea1ff7ee16b243 Mon Sep 17 00:00:00 2001 From: JJ Date: Mon, 10 Oct 2016 20:32:53 +0100 Subject: [PATCH 202/318] Fix error when a single meta value attempts to update to the current value. Adds test. If the value to be updated is the same as is currently in the database, update_metadata() returns false which leads to the error. The fix is to have the exact same check run in update_meta_value and return true if the values are the same. It doesn't update. Also included is a test for adding a duplicate value. --- lib/fields/class-wp-rest-meta-fields.php | 16 ++++++++++-- tests/test-rest-post-meta-fields.php | 31 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index ab674a42e3..b1318e2e6b 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -211,7 +211,7 @@ protected function update_multi_meta_value( $object, $name, $values ) { * * @param int $object Object ID. * @param string $name Key for the custom field. - * @return bool|WP_Error True if meta field is deleted, error otherwise. + * @return bool|WP_Error True if meta field is updated, error otherwise. */ protected function update_meta_value( $object, $name, $value ) { if ( ! current_user_can( 'edit_post_meta', $object, $name ) ) { @@ -222,7 +222,19 @@ protected function update_meta_value( $object, $name, $value ) { ); } - if ( ! update_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { + $meta_type = $this->get_meta_type(); + $meta_key = wp_slash( $name ); + $meta_value = wp_slash( $value ); + + // Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false. + $old_value = get_metadata( $meta_type, $object, $meta_key ); + if ( count( $old_value ) == 1 ) { + if ( $old_value[0] === $meta_value ){ + return true; + } + } + + if ( ! update_metadata( $meta_type, $object, $meta_key, $meta_value ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), diff --git a/tests/test-rest-post-meta-fields.php b/tests/test-rest-post-meta-fields.php index b4c3472914..305c8db848 100644 --- a/tests/test-rest-post-meta-fields.php +++ b/tests/test-rest-post-meta-fields.php @@ -229,6 +229,37 @@ public function test_set_value() { $this->assertEquals( 'test_value', $meta['test_single'] ); } + /** + * @depends test_get_value + */ + public function test_set_duplicate_single_value() { + // Start with an existing metakey and value + $values = update_post_meta( $this->post_id, 'test_single', 'test_value' ); + $this->assertEquals( 'test_value', get_post_meta( $this->post_id, 'test_single', true ) ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_single' => 'test_value', + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertNotEmpty( $meta ); + $this->assertEquals( 'test_value', $meta ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayHasKey( 'test_single', $meta ); + $this->assertEquals( 'test_value', $meta['test_single'] ); + } + /** * @depends test_set_value */ From b3c97a5fbc9a3c0bd6fb2918d9d7282c7bc74989 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Mon, 10 Oct 2016 20:52:30 -0500 Subject: [PATCH 203/318] Get the WP_User for the provided comment author and pre-populate the author data or return an error if not found --- .../class-wp-rest-comments-controller.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 3a71e615e5..4bf99746d4 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -325,6 +325,9 @@ public function create_item( $request ) { } $prepared_comment = $this->prepare_item_for_database( $request ); + if ( is_wp_error( $prepared_comment ) ) { + return $prepared_comment; + } // Setting remaining values before wp_insert_comment so we can // use wp_allow_comment(). @@ -455,6 +458,10 @@ public function update_item( $request ) { return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment status failed.' ), array( 'status' => 500 ) ); } } else { + if ( is_wp_error( $prepared_args ) ) { + return $prepared_args; + } + $prepared_args['comment_ID'] = $id; $updated = wp_update_comment( $prepared_args ); @@ -767,7 +774,15 @@ protected function prepare_item_for_database( $request ) { } if ( isset( $request['author'] ) ) { - $prepared_comment['user_id'] = $request['author']; + $user = new WP_User( $request['author'] ); + if ( $user->exists() ) { + $prepared_comment['user_id'] = $user->ID; + $prepared_comment['comment_author'] = $user->display_name; + $prepared_comment['comment_author_email'] = $user->user_email; + $prepared_comment['comment_author_url'] = $user->user_url; + } else { + return new WP_Error( 'rest_comment_author_invalid', __( 'Invalid comment author id.' ), array( 'status' => 400 ) ); + } } if ( isset( $request['author_name'] ) ) { From ef9d6d611ce651eb9ac9348f563517acee04f19f Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 11 Oct 2016 09:43:58 +0300 Subject: [PATCH 204/318] handle error codes --- .../class-wp-rest-comments-controller.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 3a71e615e5..41072b427e 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -354,7 +354,22 @@ public function create_item( $request ) { } $prepared_comment['comment_agent'] = ''; - $prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment ); + $prepared_comment['comment_approved'] = wp_allow_comment( $prepared_comment, true ); + + if ( is_wp_error( $prepared_comment['comment_approved'] ) ) { + $error_code = $prepared_comment['comment_approved']->get_error_code(); + $error_message = $prepared_comment['comment_approved']->get_error_message(); + + if ( $error_code === 'comment_duplicate' ) { + return new WP_Error( $error_code, $error_message, array( 'status' => 409 ) ); + } + + if ( $error_code === 'comment_flood' ) { + return new WP_Error( $error_code, $error_message, array( 'status' => 400 ) ); + } + + return $prepared_comment['comment_approved']; + } /** * Filter a comment before it is inserted via the REST API. From 9452bfe8c3b1c42bb70b68933b1baf753c66c0ed Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 11 Oct 2016 09:44:42 +0300 Subject: [PATCH 205/318] activate tests --- tests/test-rest-comments-controller.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 0a54d3f441..3f4952d27a 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1048,7 +1048,6 @@ public function test_create_comment_private_post_invalide_permission() { } public function test_create_item_duplicate() { - $this->markTestSkipped( 'Needs to be revisited after wp_die handling is added' ); $this->factory->comment->create( array( 'comment_post_ID' => $this->post_id, @@ -1105,8 +1104,6 @@ public function test_create_comment_require_login() { public function test_create_comment_two_times() { - $this->markTestSkipped( 'Needs to be revisited after wp_die handling is added' ); - wp_set_current_user( 0 ); $params = array( From 191614403a0277dc9a66edf679811bc5d53baaa5 Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 11 Oct 2016 17:13:40 +0300 Subject: [PATCH 206/318] wp_version control for tests --- tests/test-rest-comments-controller.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 3f4952d27a..6d1e85eb08 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1048,6 +1048,10 @@ public function test_create_comment_private_post_invalide_permission() { } public function test_create_item_duplicate() { + if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { + return $this->markTestSkipped( 'WordPress version not supported.' ); + } + $this->factory->comment->create( array( 'comment_post_ID' => $this->post_id, @@ -1103,6 +1107,9 @@ public function test_create_comment_require_login() { } public function test_create_comment_two_times() { + if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { + return $this->markTestSkipped( 'WordPress version not supported.' ); + } wp_set_current_user( 0 ); From 90117f01359b32affbddd5bb649ca468ea2af30c Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 11 Oct 2016 17:17:47 +0300 Subject: [PATCH 207/318] Use Yoda Condition checks, you must --- lib/endpoints/class-wp-rest-comments-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 41072b427e..2c26454f93 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -360,11 +360,11 @@ public function create_item( $request ) { $error_code = $prepared_comment['comment_approved']->get_error_code(); $error_message = $prepared_comment['comment_approved']->get_error_message(); - if ( $error_code === 'comment_duplicate' ) { + if ( 'comment_duplicate' === $error_code ) { return new WP_Error( $error_code, $error_message, array( 'status' => 409 ) ); } - if ( $error_code === 'comment_flood' ) { + if ( 'comment_flood' === $error_code ) { return new WP_Error( $error_code, $error_message, array( 'status' => 400 ) ); } From df4fa19069b1cf738bc265c62bda09d4df12d07c Mon Sep 17 00:00:00 2001 From: websupporter Date: Tue, 11 Oct 2016 17:29:13 +0300 Subject: [PATCH 208/318] global --- tests/test-rest-comments-controller.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 6d1e85eb08..d4eff05277 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1048,6 +1048,7 @@ public function test_create_comment_private_post_invalide_permission() { } public function test_create_item_duplicate() { + global $wp_version; if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { return $this->markTestSkipped( 'WordPress version not supported.' ); } @@ -1107,6 +1108,7 @@ public function test_create_comment_require_login() { } public function test_create_comment_two_times() { + global $wp_version; if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { return $this->markTestSkipped( 'WordPress version not supported.' ); } From 11c5d1e68369a05dfad2afad56d2841124051d56 Mon Sep 17 00:00:00 2001 From: JJ Date: Tue, 11 Oct 2016 18:58:36 +0100 Subject: [PATCH 209/318] Fixed styling issues. Maybe. Fingers crossed here. --- lib/fields/class-wp-rest-meta-fields.php | 4 +- tests/test-rest-post-meta-fields.php | 554 +++++++++++------------ 2 files changed, 279 insertions(+), 279 deletions(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index b1318e2e6b..b94e4c6a92 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -229,12 +229,12 @@ protected function update_meta_value( $object, $name, $value ) { // Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false. $old_value = get_metadata( $meta_type, $object, $meta_key ); if ( count( $old_value ) == 1 ) { - if ( $old_value[0] === $meta_value ){ + if ( $old_value[0] === $meta_value ) { return true; } } - if ( ! update_metadata( $meta_type, $object, $meta_key, $meta_value ) ) { + if ( ! update_metadata( $meta_type, $object, $meta_key, $meta_value ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), diff --git a/tests/test-rest-post-meta-fields.php b/tests/test-rest-post-meta-fields.php index 305c8db848..e75b77c360 100644 --- a/tests/test-rest-post-meta-fields.php +++ b/tests/test-rest-post-meta-fields.php @@ -230,404 +230,404 @@ public function test_set_value() { } /** - * @depends test_get_value - */ - public function test_set_duplicate_single_value() { - // Start with an existing metakey and value - $values = update_post_meta( $this->post_id, 'test_single', 'test_value' ); - $this->assertEquals( 'test_value', get_post_meta( $this->post_id, 'test_single', true ) ); - - $this->grant_write_permission(); - - $data = array( - 'meta' => array( - 'test_single' => 'test_value', - ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); - - $meta = get_post_meta( $this->post_id, 'test_single', true ); - $this->assertNotEmpty( $meta ); - $this->assertEquals( 'test_value', $meta ); - - $data = $response->get_data(); - $meta = (array) $data['meta']; - $this->assertArrayHasKey( 'test_single', $meta ); - $this->assertEquals( 'test_value', $meta['test_single'] ); - } - - /** - * @depends test_set_value + * @depends test_get_value */ - public function test_set_value_unauthenticated() { - $data = array( + public function test_set_duplicate_single_value() { + // Start with an existing metakey and value + $values = update_post_meta( $this->post_id, 'test_single', 'test_value' ); + $this->assertEquals( 'test_value', get_post_meta( $this->post_id, 'test_single', true ) ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_single' => 'test_value', + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertNotEmpty( $meta ); + $this->assertEquals( 'test_value', $meta ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayHasKey( 'test_single', $meta ); + $this->assertEquals( 'test_value', $meta['test_single'] ); + } + + /** + * @depends test_set_value + */ + public function test_set_value_unauthenticated() { + $data = array( 'meta' => array( 'test_single' => 'test_value', ), - ); + ); - wp_set_current_user( 0 ); + wp_set_current_user( 0 ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); - // Check that the value wasn't actually updated. - $this->assertEmpty( get_post_meta( $this->post_id, 'test_single', false ) ); - } + // Check that the value wasn't actually updated. + $this->assertEmpty( get_post_meta( $this->post_id, 'test_single', false ) ); + } - /** - * @depends test_set_value - */ - public function test_set_value_blocked() { - $data = array( + /** + * @depends test_set_value + */ + public function test_set_value_blocked() { + $data = array( 'meta' => array( 'test_bad_auth' => 'test_value', ), - ); + ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); - $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth', false ) ); - } + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); + $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth', false ) ); + } - /** - * @depends test_set_value - */ - public function test_set_value_db_error() { - $data = array( + /** + * @depends test_set_value + */ + public function test_set_value_db_error() { + $data = array( 'meta' => array( 'test_single' => 'test_value', ), - ); + ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - /** + /** * Disable showing error as the below is going to intentionally * trigger a DB error. */ - global $wpdb; - $wpdb->suppress_errors = true; - add_filter( 'query', array( $this, 'error_insert_query' ) ); + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_insert_query' ) ); - $response = $this->server->dispatch( $request ); - remove_filter( 'query', array( $this, 'error_insert_query' ) ); - $wpdb->show_errors = true; - } + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_insert_query' ) ); + $wpdb->show_errors = true; + } - public function test_set_value_multiple() { - // Ensure no data exists currently. - $values = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEmpty( $values ); + public function test_set_value_multiple() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array( 'val1' ), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); - $meta = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertNotEmpty( $meta ); - $this->assertCount( 1, $meta ); - $this->assertEquals( 'val1', $meta[0] ); + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 1, $meta ); + $this->assertEquals( 'val1', $meta[0] ); - // Add another value. - $data = array( + // Add another value. + $data = array( 'meta' => array( 'test_multi' => array( 'val1', 'val2' ), ), - ); - $request->set_body_params( $data ); + ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); - $meta = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertNotEmpty( $meta ); - $this->assertCount( 2, $meta ); - $this->assertContains( 'val1', $meta ); - $this->assertContains( 'val2', $meta ); - } + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 2, $meta ); + $this->assertContains( 'val1', $meta ); + $this->assertContains( 'val2', $meta ); + } - /** - * Test removing only one item with duplicate items. - */ - public function test_set_value_remove_one() { - add_post_meta( $this->post_id, 'test_multi', 'c' ); - add_post_meta( $this->post_id, 'test_multi', 'n' ); - add_post_meta( $this->post_id, 'test_multi', 'n' ); + /** + * Test removing only one item with duplicate items. + */ + public function test_set_value_remove_one() { + add_post_meta( $this->post_id, 'test_multi', 'c' ); + add_post_meta( $this->post_id, 'test_multi', 'n' ); + add_post_meta( $this->post_id, 'test_multi', 'n' ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array( 'c', 'n' ), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); - - $meta = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertNotEmpty( $meta ); - $this->assertCount( 2, $meta ); - $this->assertContains( 'c', $meta ); - $this->assertContains( 'n', $meta ); - } + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 2, $meta ); + $this->assertContains( 'c', $meta ); + $this->assertContains( 'n', $meta ); + } - /** - * @depends test_set_value_multiple - */ - public function test_set_value_multiple_unauthenticated() { - // Ensure no data exists currently. - $values = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEmpty( $values ); + /** + * @depends test_set_value_multiple + */ + public function test_set_value_multiple_unauthenticated() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); - wp_set_current_user( 0 ); + wp_set_current_user( 0 ); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array( 'val1' ), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); - $meta = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEmpty( $meta ); - } + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $meta ); + } - /** - * @depends test_set_value_multiple - */ - public function test_set_value_multiple_blocked() { - $data = array( + /** + * @depends test_set_value_multiple + */ + public function test_set_value_multiple_blocked() { + $data = array( 'meta' => array( 'test_bad_auth_multi' => array( 'test_value' ), ), - ); + ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); - $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth_multi', false ) ); - } + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); + $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth_multi', false ) ); + } - public function test_add_multi_value_db_error() { - // Ensure no data exists currently. - $values = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEmpty( $values ); + public function test_add_multi_value_db_error() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array( 'val1' ), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - /** + /** * Disable showing error as the below is going to intentionally * trigger a DB error. */ - global $wpdb; - $wpdb->suppress_errors = true; - add_filter( 'query', array( $this, 'error_insert_query' ) ); + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_insert_query' ) ); - $response = $this->server->dispatch( $request ); - remove_filter( 'query', array( $this, 'error_insert_query' ) ); - $wpdb->show_errors = true; + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_insert_query' ) ); + $wpdb->show_errors = true; - $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); - } + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } - public function test_remove_multi_value_db_error() { - add_post_meta( $this->post_id, 'test_multi', 'val1' ); - $values = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEquals( array( 'val1' ), $values ); + public function test_remove_multi_value_db_error() { + add_post_meta( $this->post_id, 'test_multi', 'val1' ); + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEquals( array( 'val1' ), $values ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array(), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - /** + /** * Disable showing error as the below is going to intentionally * trigger a DB error. */ - global $wpdb; - $wpdb->suppress_errors = true; - add_filter( 'query', array( $this, 'error_delete_query' ) ); + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_delete_query' ) ); - $response = $this->server->dispatch( $request ); - remove_filter( 'query', array( $this, 'error_delete_query' ) ); - $wpdb->show_errors = true; + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_delete_query' ) ); + $wpdb->show_errors = true; - $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); - } + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } - public function test_delete_value() { - add_post_meta( $this->post_id, 'test_single', 'val1' ); - $current = get_post_meta( $this->post_id, 'test_single', true ); - $this->assertEquals( 'val1', $current ); + public function test_delete_value() { + add_post_meta( $this->post_id, 'test_single', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertEquals( 'val1', $current ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_single' => null, ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); - $meta = get_post_meta( $this->post_id, 'test_single', false ); - $this->assertEmpty( $meta ); - } + $meta = get_post_meta( $this->post_id, 'test_single', false ); + $this->assertEmpty( $meta ); + } - /** - * @depends test_delete_value - */ - public function test_delete_value_blocked() { - add_post_meta( $this->post_id, 'test_bad_auth', 'val1' ); - $current = get_post_meta( $this->post_id, 'test_bad_auth', true ); - $this->assertEquals( 'val1', $current ); + /** + * @depends test_delete_value + */ + public function test_delete_value_blocked() { + add_post_meta( $this->post_id, 'test_bad_auth', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_bad_auth', true ); + $this->assertEquals( 'val1', $current ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_bad_auth' => null, ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); - $meta = get_post_meta( $this->post_id, 'test_bad_auth', true ); - $this->assertEquals( 'val1', $meta ); - } + $meta = get_post_meta( $this->post_id, 'test_bad_auth', true ); + $this->assertEquals( 'val1', $meta ); + } - /** - * @depends test_delete_value - */ - public function test_delete_value_db_error() { - add_post_meta( $this->post_id, 'test_single', 'val1' ); - $current = get_post_meta( $this->post_id, 'test_single', true ); - $this->assertEquals( 'val1', $current ); + /** + * @depends test_delete_value + */ + public function test_delete_value_db_error() { + add_post_meta( $this->post_id, 'test_single', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertEquals( 'val1', $current ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_single' => null, ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); - /** + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + /** * Disable showing error as the below is going to intentionally * trigger a DB error. */ - global $wpdb; - $wpdb->suppress_errors = true; - add_filter( 'query', array( $this, 'error_delete_query' ) ); + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_delete_query' ) ); - $response = $this->server->dispatch( $request ); - remove_filter( 'query', array( $this, 'error_delete_query' ) ); - $wpdb->show_errors = true; + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_delete_query' ) ); + $wpdb->show_errors = true; - $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); - } + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } - public function test_get_schema() { - $request = new WP_REST_Request( 'OPTIONS', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $response = $this->server->dispatch( $request ); + public function test_get_schema() { + $request = new WP_REST_Request( 'OPTIONS', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $response = $this->server->dispatch( $request ); - $data = $response->get_data(); - $schema = $data['schema']; + $data = $response->get_data(); + $schema = $data['schema']; - $this->assertArrayHasKey( 'meta', $schema['properties'] ); - $meta_schema = $schema['properties']['meta']['properties']; + $this->assertArrayHasKey( 'meta', $schema['properties'] ); + $meta_schema = $schema['properties']['meta']['properties']; - $this->assertArrayHasKey( 'test_single', $meta_schema ); - $this->assertEquals( 'string', $meta_schema['test_single']['type'] ); + $this->assertArrayHasKey( 'test_single', $meta_schema ); + $this->assertEquals( 'string', $meta_schema['test_single']['type'] ); - $this->assertArrayHasKey( 'test_multi', $meta_schema ); - $this->assertEquals( 'array', $meta_schema['test_multi']['type'] ); - $this->assertArrayHasKey( 'items', $meta_schema['test_multi'] ); - $this->assertEquals( 'string', $meta_schema['test_multi']['items']['type'] ); + $this->assertArrayHasKey( 'test_multi', $meta_schema ); + $this->assertEquals( 'array', $meta_schema['test_multi']['type'] ); + $this->assertArrayHasKey( 'items', $meta_schema['test_multi'] ); + $this->assertEquals( 'string', $meta_schema['test_multi']['items']['type'] ); - $this->assertArrayHasKey( 'test_custom_schema', $meta_schema ); - $this->assertEquals( 'number', $meta_schema['test_custom_schema']['type'] ); + $this->assertArrayHasKey( 'test_custom_schema', $meta_schema ); + $this->assertEquals( 'number', $meta_schema['test_custom_schema']['type'] ); - $this->assertArrayNotHasKey( 'test_no_rest', $meta_schema ); - $this->assertArrayNotHasKey( 'test_rest_disabled', $meta_schema ); - $this->assertArrayNotHasKey( 'test_invalid_type', $meta_schema ); - } + $this->assertArrayNotHasKey( 'test_no_rest', $meta_schema ); + $this->assertArrayNotHasKey( 'test_rest_disabled', $meta_schema ); + $this->assertArrayNotHasKey( 'test_invalid_type', $meta_schema ); + } - /** - * Internal function used to disable an insert query which - * will trigger a wpdb error for testing purposes. - */ - public function error_insert_query( $query ) { - if ( strpos( $query, 'INSERT' ) === 0 ) { - $query = '],'; + /** + * Internal function used to disable an insert query which + * will trigger a wpdb error for testing purposes. + */ + public function error_insert_query( $query ) { + if ( strpos( $query, 'INSERT' ) === 0 ) { + $query = '],'; + } + return $query; } - return $query; - } - /** - * Internal function used to disable an insert query which - * will trigger a wpdb error for testing purposes. - */ - public function error_delete_query( $query ) { - if ( strpos( $query, 'DELETE' ) === 0 ) { - $query = '],'; + /** + * Internal function used to disable an insert query which + * will trigger a wpdb error for testing purposes. + */ + public function error_delete_query( $query ) { + if ( strpos( $query, 'DELETE' ) === 0 ) { + $query = '],'; + } + return $query; } - return $query; - } } From 64f6753656f61fc63449f3512b6d377d101f3342 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 11 Oct 2016 15:10:46 -0400 Subject: [PATCH 210/318] Check known collection query parameters in a loop rather than one-by-one This tightens up the patch in #2770 to use an associative array of public api-to-internal-wp query parameters, reducing the repetition of if blocks in the code. The array only describes those properties that to not require special handling; others (like `before` or `filter`) are still checked against `$registered` individually because their values may not be set as-passed. --- .../class-wp-rest-posts-controller.php | 83 ++++++++----------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 613c2e4b51..4bf677d19b 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -94,70 +94,59 @@ public function get_items( $request ) { return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) ); } + // Retrieve the list of registered collection query parameters. $registered = $this->get_collection_params(); - $args = array(); + $args = array(); + + // This array defines mappings between public API query parameters whose + // values are accepted as-passed, and their internal WP_Query parameter + // name equivalents (some are the same). Only values which are also + // present in $registered will be set. + $parameter_mappings = array( + 'author' => 'author__in', + 'author_exclude' => 'author__not_in', + 'exclude' => 'post__not_in', + 'include' => 'post__in', + 'menu_order' => 'menu_order', + 'offset' => 'offset', + 'order' => 'order', + 'orderby' => 'orderby', + 'page' => 'paged', + 'parent' => 'post_parent__in', + 'parent_exclude' => 'post_parent__not_in', + 'search' => 's', + 'slug' => 'name', + 'status' => 'post_status', + ); - if ( isset( $registered['offset'] ) ) { - $args['offset'] = $request['offset']; - } - if ( isset( $registered['order'] ) ) { - $args['order'] = $request['order']; - } - if ( isset( $registered['orderby'] ) ) { - $args['orderby'] = $request['orderby']; - } - if ( isset( $registered['page'] ) ) { - $args['paged'] = $request['page']; - } - if ( isset( $registered['include'] ) ) { - $args['post__in'] = $request['include']; - } - if ( isset( $registered['exclude'] ) ) { - $args['post__not_in'] = $request['exclude']; - } - if ( isset( $registered['slug'] ) ) { - $args['name'] = $request['slug']; - } - if ( isset( $registered['status'] ) ) { - $args['post_status'] = $request['status']; - } - if ( isset( $registered['search'] ) ) { - $args['s'] = $request['search']; - } - if ( isset( $registered['author'] ) ) { - $args['author__in'] = $request['author']; - } - if ( isset( $registered['author_exclude'] ) ) { - $args['author__not_in'] = $request['author_exclude']; - } - if ( isset( $registered['menu_order'] ) ) { - $args['menu_order'] = $request['menu_order']; - } - if ( isset( $registered['parent'] ) ) { - $args['post_parent__in'] = $request['parent']; - } - if ( isset( $registered['parent_exclude'] ) ) { - $args['post_parent__not_in'] = $request['parent_exclude']; + // For each known parameter which is both registered and present in the request, + // set the parameter's value on the query $args. + foreach ( $parameter_mappings as $api_param => $wp_param ) { + if ( isset( $registered[ $api_param ] ) && isset( $request[ $api_param ] ) ) { + $args[ $wp_param ] = $request[ $api_param ]; + } } + // Check for & assign any parameters which require special handling or setting. + $args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. - if ( isset( $registered['before'] && isset( $request['before'] ) ) { + if ( isset( $registered['before'] ) && isset( $request['before'] ) ) { $args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. - if ( isset( $registered['after'] && isset( $request['after'] ) ) { + if ( isset( $registered['after'] ) && isset( $request['after'] ) ) { $args['date_query'][0]['after'] = $request['after']; } - if ( isset( $registered['filter'] && is_array( $request['filter'] ) ) { + if ( isset( $registered['filter'] ) && is_array( $request['filter'] ) ) { $args = array_merge( $args, $request['filter'] ); unset( $args['filter'] ); } - // Ensure our per_page parameter overrides filter. - if ( isset( $registered['per_page'] ) { + // Ensure our per_page parameter overrides any provided posts_per_page filter. + if ( isset( $registered['per_page'] ) ) { $args['posts_per_page'] = $request['per_page']; } From 82b5f57b5c6565fe8a325dc5ac1afaf18aa2aa5c Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 11 Oct 2016 16:43:27 -0400 Subject: [PATCH 211/318] Properly register "{taxonomy}_exclude" parameters in the schema PR #2756 added an `_exclude` complement to every registered taxonomy query parameter method, e.g. `categories_exclude`, to permit an API consumer to list an array of term IDs with which returned posts should NOT be associated. However, that PR omitted the important step of registering the parameters on the schema! This PR does so. --- lib/endpoints/class-wp-rest-posts-controller.php | 5 +++++ tests/test-rest-posts-controller.php | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 830dfc68b7..1f564bba6d 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1746,6 +1746,11 @@ public function get_item_schema() { 'type' => 'array', 'context' => array( 'view', 'edit' ), ); + $schema['properties'][ $base . '_exclude' ] = array( + 'description' => sprintf( __( 'The terms in the %s taxonomy that should not be assigned to the object.' ), $taxonomy->name ), + 'type' => 'array', + 'context' => array( 'view', 'edit' ), + ); } return $this->add_additional_fields_schema( $schema ); diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 6b595428e0..66832ed612 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -1816,7 +1816,7 @@ public function test_get_item_schema() { $response = $this->server->dispatch( $request ); $data = $response->get_data(); $properties = $data['schema']['properties']; - $this->assertEquals( 23, count( $properties ) ); + $this->assertEquals( 25, count( $properties ) ); $this->assertArrayHasKey( 'author', $properties ); $this->assertArrayHasKey( 'comment_status', $properties ); $this->assertArrayHasKey( 'content', $properties ); @@ -1839,7 +1839,9 @@ public function test_get_item_schema() { $this->assertArrayHasKey( 'title', $properties ); $this->assertArrayHasKey( 'type', $properties ); $this->assertArrayHasKey( 'tags', $properties ); + $this->assertArrayHasKey( 'tags_exclude', $properties ); $this->assertArrayHasKey( 'categories', $properties ); + $this->assertArrayHasKey( 'categories_exclude', $properties ); } public function test_get_additional_field_registration() { From 42649c30677d8b58201e28d252ed8e7b5c1956e2 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Tue, 11 Oct 2016 20:34:22 -0500 Subject: [PATCH 212/318] Revert "Fixed styling issues. Maybe." This reverts commit 11c5d1e68369a05dfad2afad56d2841124051d56. --- lib/fields/class-wp-rest-meta-fields.php | 4 +- tests/test-rest-post-meta-fields.php | 554 +++++++++++------------ 2 files changed, 279 insertions(+), 279 deletions(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index b94e4c6a92..b1318e2e6b 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -229,12 +229,12 @@ protected function update_meta_value( $object, $name, $value ) { // Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false. $old_value = get_metadata( $meta_type, $object, $meta_key ); if ( count( $old_value ) == 1 ) { - if ( $old_value[0] === $meta_value ) { + if ( $old_value[0] === $meta_value ){ return true; } } - if ( ! update_metadata( $meta_type, $object, $meta_key, $meta_value ) ) { + if ( ! update_metadata( $meta_type, $object, $meta_key, $meta_value ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), diff --git a/tests/test-rest-post-meta-fields.php b/tests/test-rest-post-meta-fields.php index e75b77c360..305c8db848 100644 --- a/tests/test-rest-post-meta-fields.php +++ b/tests/test-rest-post-meta-fields.php @@ -230,404 +230,404 @@ public function test_set_value() { } /** - * @depends test_get_value + * @depends test_get_value + */ + public function test_set_duplicate_single_value() { + // Start with an existing metakey and value + $values = update_post_meta( $this->post_id, 'test_single', 'test_value' ); + $this->assertEquals( 'test_value', get_post_meta( $this->post_id, 'test_single', true ) ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_single' => 'test_value', + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertNotEmpty( $meta ); + $this->assertEquals( 'test_value', $meta ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayHasKey( 'test_single', $meta ); + $this->assertEquals( 'test_value', $meta['test_single'] ); + } + + /** + * @depends test_set_value */ - public function test_set_duplicate_single_value() { - // Start with an existing metakey and value - $values = update_post_meta( $this->post_id, 'test_single', 'test_value' ); - $this->assertEquals( 'test_value', get_post_meta( $this->post_id, 'test_single', true ) ); - - $this->grant_write_permission(); - - $data = array( - 'meta' => array( - 'test_single' => 'test_value', - ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); - - $meta = get_post_meta( $this->post_id, 'test_single', true ); - $this->assertNotEmpty( $meta ); - $this->assertEquals( 'test_value', $meta ); - - $data = $response->get_data(); - $meta = (array) $data['meta']; - $this->assertArrayHasKey( 'test_single', $meta ); - $this->assertEquals( 'test_value', $meta['test_single'] ); - } - - /** - * @depends test_set_value - */ - public function test_set_value_unauthenticated() { - $data = array( + public function test_set_value_unauthenticated() { + $data = array( 'meta' => array( 'test_single' => 'test_value', ), - ); + ); - wp_set_current_user( 0 ); + wp_set_current_user( 0 ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); - // Check that the value wasn't actually updated. - $this->assertEmpty( get_post_meta( $this->post_id, 'test_single', false ) ); - } + // Check that the value wasn't actually updated. + $this->assertEmpty( get_post_meta( $this->post_id, 'test_single', false ) ); + } - /** - * @depends test_set_value - */ - public function test_set_value_blocked() { - $data = array( + /** + * @depends test_set_value + */ + public function test_set_value_blocked() { + $data = array( 'meta' => array( 'test_bad_auth' => 'test_value', ), - ); + ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); - $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth', false ) ); - } + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); + $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth', false ) ); + } - /** - * @depends test_set_value - */ - public function test_set_value_db_error() { - $data = array( + /** + * @depends test_set_value + */ + public function test_set_value_db_error() { + $data = array( 'meta' => array( 'test_single' => 'test_value', ), - ); + ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - /** + /** * Disable showing error as the below is going to intentionally * trigger a DB error. */ - global $wpdb; - $wpdb->suppress_errors = true; - add_filter( 'query', array( $this, 'error_insert_query' ) ); + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_insert_query' ) ); - $response = $this->server->dispatch( $request ); - remove_filter( 'query', array( $this, 'error_insert_query' ) ); - $wpdb->show_errors = true; - } + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_insert_query' ) ); + $wpdb->show_errors = true; + } - public function test_set_value_multiple() { - // Ensure no data exists currently. - $values = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEmpty( $values ); + public function test_set_value_multiple() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array( 'val1' ), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); - $meta = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertNotEmpty( $meta ); - $this->assertCount( 1, $meta ); - $this->assertEquals( 'val1', $meta[0] ); + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 1, $meta ); + $this->assertEquals( 'val1', $meta[0] ); - // Add another value. - $data = array( + // Add another value. + $data = array( 'meta' => array( 'test_multi' => array( 'val1', 'val2' ), ), - ); - $request->set_body_params( $data ); + ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); - $meta = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertNotEmpty( $meta ); - $this->assertCount( 2, $meta ); - $this->assertContains( 'val1', $meta ); - $this->assertContains( 'val2', $meta ); - } + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 2, $meta ); + $this->assertContains( 'val1', $meta ); + $this->assertContains( 'val2', $meta ); + } - /** - * Test removing only one item with duplicate items. - */ - public function test_set_value_remove_one() { - add_post_meta( $this->post_id, 'test_multi', 'c' ); - add_post_meta( $this->post_id, 'test_multi', 'n' ); - add_post_meta( $this->post_id, 'test_multi', 'n' ); + /** + * Test removing only one item with duplicate items. + */ + public function test_set_value_remove_one() { + add_post_meta( $this->post_id, 'test_multi', 'c' ); + add_post_meta( $this->post_id, 'test_multi', 'n' ); + add_post_meta( $this->post_id, 'test_multi', 'n' ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array( 'c', 'n' ), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); - - $meta = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertNotEmpty( $meta ); - $this->assertCount( 2, $meta ); - $this->assertContains( 'c', $meta ); - $this->assertContains( 'n', $meta ); - } + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - /** - * @depends test_set_value_multiple - */ - public function test_set_value_multiple_unauthenticated() { - // Ensure no data exists currently. - $values = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEmpty( $values ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); - wp_set_current_user( 0 ); + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertNotEmpty( $meta ); + $this->assertCount( 2, $meta ); + $this->assertContains( 'c', $meta ); + $this->assertContains( 'n', $meta ); + } + + /** + * @depends test_set_value_multiple + */ + public function test_set_value_multiple_unauthenticated() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); + + wp_set_current_user( 0 ); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array( 'val1' ), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_edit', $response, 401 ); - $meta = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEmpty( $meta ); - } + $meta = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $meta ); + } - /** - * @depends test_set_value_multiple - */ - public function test_set_value_multiple_blocked() { - $data = array( + /** + * @depends test_set_value_multiple + */ + public function test_set_value_multiple_blocked() { + $data = array( 'meta' => array( 'test_bad_auth_multi' => array( 'test_value' ), ), - ); + ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); - $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth_multi', false ) ); - } + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_update', $response, 403 ); + $this->assertEmpty( get_post_meta( $this->post_id, 'test_bad_auth_multi', false ) ); + } - public function test_add_multi_value_db_error() { - // Ensure no data exists currently. - $values = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEmpty( $values ); + public function test_add_multi_value_db_error() { + // Ensure no data exists currently. + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEmpty( $values ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array( 'val1' ), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - /** + /** * Disable showing error as the below is going to intentionally * trigger a DB error. */ - global $wpdb; - $wpdb->suppress_errors = true; - add_filter( 'query', array( $this, 'error_insert_query' ) ); + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_insert_query' ) ); - $response = $this->server->dispatch( $request ); - remove_filter( 'query', array( $this, 'error_insert_query' ) ); - $wpdb->show_errors = true; + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_insert_query' ) ); + $wpdb->show_errors = true; - $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); - } + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } - public function test_remove_multi_value_db_error() { - add_post_meta( $this->post_id, 'test_multi', 'val1' ); - $values = get_post_meta( $this->post_id, 'test_multi', false ); - $this->assertEquals( array( 'val1' ), $values ); + public function test_remove_multi_value_db_error() { + add_post_meta( $this->post_id, 'test_multi', 'val1' ); + $values = get_post_meta( $this->post_id, 'test_multi', false ); + $this->assertEquals( array( 'val1' ), $values ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_multi' => array(), ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - /** + /** * Disable showing error as the below is going to intentionally * trigger a DB error. */ - global $wpdb; - $wpdb->suppress_errors = true; - add_filter( 'query', array( $this, 'error_delete_query' ) ); + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_delete_query' ) ); - $response = $this->server->dispatch( $request ); - remove_filter( 'query', array( $this, 'error_delete_query' ) ); - $wpdb->show_errors = true; + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_delete_query' ) ); + $wpdb->show_errors = true; - $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); - } + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } - public function test_delete_value() { - add_post_meta( $this->post_id, 'test_single', 'val1' ); - $current = get_post_meta( $this->post_id, 'test_single', true ); - $this->assertEquals( 'val1', $current ); + public function test_delete_value() { + add_post_meta( $this->post_id, 'test_single', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertEquals( 'val1', $current ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_single' => null, ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); - $meta = get_post_meta( $this->post_id, 'test_single', false ); - $this->assertEmpty( $meta ); - } + $meta = get_post_meta( $this->post_id, 'test_single', false ); + $this->assertEmpty( $meta ); + } - /** - * @depends test_delete_value - */ - public function test_delete_value_blocked() { - add_post_meta( $this->post_id, 'test_bad_auth', 'val1' ); - $current = get_post_meta( $this->post_id, 'test_bad_auth', true ); - $this->assertEquals( 'val1', $current ); + /** + * @depends test_delete_value + */ + public function test_delete_value_blocked() { + add_post_meta( $this->post_id, 'test_bad_auth', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_bad_auth', true ); + $this->assertEquals( 'val1', $current ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_bad_auth' => null, ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); - $response = $this->server->dispatch( $request ); - $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 ); - $meta = get_post_meta( $this->post_id, 'test_bad_auth', true ); - $this->assertEquals( 'val1', $meta ); - } + $meta = get_post_meta( $this->post_id, 'test_bad_auth', true ); + $this->assertEquals( 'val1', $meta ); + } - /** - * @depends test_delete_value - */ - public function test_delete_value_db_error() { - add_post_meta( $this->post_id, 'test_single', 'val1' ); - $current = get_post_meta( $this->post_id, 'test_single', true ); - $this->assertEquals( 'val1', $current ); + /** + * @depends test_delete_value + */ + public function test_delete_value_db_error() { + add_post_meta( $this->post_id, 'test_single', 'val1' ); + $current = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertEquals( 'val1', $current ); - $this->grant_write_permission(); + $this->grant_write_permission(); - $data = array( + $data = array( 'meta' => array( 'test_single' => null, ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); - /** + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + /** * Disable showing error as the below is going to intentionally * trigger a DB error. */ - global $wpdb; - $wpdb->suppress_errors = true; - add_filter( 'query', array( $this, 'error_delete_query' ) ); + global $wpdb; + $wpdb->suppress_errors = true; + add_filter( 'query', array( $this, 'error_delete_query' ) ); - $response = $this->server->dispatch( $request ); - remove_filter( 'query', array( $this, 'error_delete_query' ) ); - $wpdb->show_errors = true; + $response = $this->server->dispatch( $request ); + remove_filter( 'query', array( $this, 'error_delete_query' ) ); + $wpdb->show_errors = true; - $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); - } + $this->assertErrorResponse( 'rest_meta_database_error', $response, 500 ); + } - public function test_get_schema() { - $request = new WP_REST_Request( 'OPTIONS', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $response = $this->server->dispatch( $request ); + public function test_get_schema() { + $request = new WP_REST_Request( 'OPTIONS', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $response = $this->server->dispatch( $request ); - $data = $response->get_data(); - $schema = $data['schema']; + $data = $response->get_data(); + $schema = $data['schema']; - $this->assertArrayHasKey( 'meta', $schema['properties'] ); - $meta_schema = $schema['properties']['meta']['properties']; + $this->assertArrayHasKey( 'meta', $schema['properties'] ); + $meta_schema = $schema['properties']['meta']['properties']; - $this->assertArrayHasKey( 'test_single', $meta_schema ); - $this->assertEquals( 'string', $meta_schema['test_single']['type'] ); + $this->assertArrayHasKey( 'test_single', $meta_schema ); + $this->assertEquals( 'string', $meta_schema['test_single']['type'] ); - $this->assertArrayHasKey( 'test_multi', $meta_schema ); - $this->assertEquals( 'array', $meta_schema['test_multi']['type'] ); - $this->assertArrayHasKey( 'items', $meta_schema['test_multi'] ); - $this->assertEquals( 'string', $meta_schema['test_multi']['items']['type'] ); + $this->assertArrayHasKey( 'test_multi', $meta_schema ); + $this->assertEquals( 'array', $meta_schema['test_multi']['type'] ); + $this->assertArrayHasKey( 'items', $meta_schema['test_multi'] ); + $this->assertEquals( 'string', $meta_schema['test_multi']['items']['type'] ); - $this->assertArrayHasKey( 'test_custom_schema', $meta_schema ); - $this->assertEquals( 'number', $meta_schema['test_custom_schema']['type'] ); + $this->assertArrayHasKey( 'test_custom_schema', $meta_schema ); + $this->assertEquals( 'number', $meta_schema['test_custom_schema']['type'] ); - $this->assertArrayNotHasKey( 'test_no_rest', $meta_schema ); - $this->assertArrayNotHasKey( 'test_rest_disabled', $meta_schema ); - $this->assertArrayNotHasKey( 'test_invalid_type', $meta_schema ); - } + $this->assertArrayNotHasKey( 'test_no_rest', $meta_schema ); + $this->assertArrayNotHasKey( 'test_rest_disabled', $meta_schema ); + $this->assertArrayNotHasKey( 'test_invalid_type', $meta_schema ); + } - /** - * Internal function used to disable an insert query which - * will trigger a wpdb error for testing purposes. - */ - public function error_insert_query( $query ) { - if ( strpos( $query, 'INSERT' ) === 0 ) { - $query = '],'; - } - return $query; + /** + * Internal function used to disable an insert query which + * will trigger a wpdb error for testing purposes. + */ + public function error_insert_query( $query ) { + if ( strpos( $query, 'INSERT' ) === 0 ) { + $query = '],'; } + return $query; + } - /** - * Internal function used to disable an insert query which - * will trigger a wpdb error for testing purposes. - */ - public function error_delete_query( $query ) { - if ( strpos( $query, 'DELETE' ) === 0 ) { - $query = '],'; - } - return $query; + /** + * Internal function used to disable an insert query which + * will trigger a wpdb error for testing purposes. + */ + public function error_delete_query( $query ) { + if ( strpos( $query, 'DELETE' ) === 0 ) { + $query = '],'; } + return $query; + } } From 325af1ffad331da580ac7fbc985ff259bab37719 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Tue, 11 Oct 2016 20:40:08 -0500 Subject: [PATCH 213/318] Fix code standards issues from our PHPCS rules --- lib/fields/class-wp-rest-meta-fields.php | 10 ++-- tests/test-rest-post-meta-fields.php | 60 ++++++++++++------------ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index b1318e2e6b..ed9569bc2b 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -222,14 +222,14 @@ protected function update_meta_value( $object, $name, $value ) { ); } - $meta_type = $this->get_meta_type(); - $meta_key = wp_slash( $name ); - $meta_value = wp_slash( $value ); + $meta_type = $this->get_meta_type(); + $meta_key = wp_slash( $name ); + $meta_value = wp_slash( $value ); // Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false. $old_value = get_metadata( $meta_type, $object, $meta_key ); - if ( count( $old_value ) == 1 ) { - if ( $old_value[0] === $meta_value ){ + if ( 1 === count( $old_value ) ) { + if ( $old_value[0] === $meta_value ) { return true; } } diff --git a/tests/test-rest-post-meta-fields.php b/tests/test-rest-post-meta-fields.php index 305c8db848..e6103015d6 100644 --- a/tests/test-rest-post-meta-fields.php +++ b/tests/test-rest-post-meta-fields.php @@ -230,36 +230,36 @@ public function test_set_value() { } /** - * @depends test_get_value - */ - public function test_set_duplicate_single_value() { - // Start with an existing metakey and value - $values = update_post_meta( $this->post_id, 'test_single', 'test_value' ); - $this->assertEquals( 'test_value', get_post_meta( $this->post_id, 'test_single', true ) ); - - $this->grant_write_permission(); - - $data = array( - 'meta' => array( - 'test_single' => 'test_value', - ), - ); - $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); - $request->set_body_params( $data ); - - $response = $this->server->dispatch( $request ); - $this->assertEquals( 200, $response->get_status() ); - - $meta = get_post_meta( $this->post_id, 'test_single', true ); - $this->assertNotEmpty( $meta ); - $this->assertEquals( 'test_value', $meta ); - - $data = $response->get_data(); - $meta = (array) $data['meta']; - $this->assertArrayHasKey( 'test_single', $meta ); - $this->assertEquals( 'test_value', $meta['test_single'] ); - } - + * @depends test_get_value + */ + public function test_set_duplicate_single_value() { + // Start with an existing metakey and value. + $values = update_post_meta( $this->post_id, 'test_single', 'test_value' ); + $this->assertEquals( 'test_value', get_post_meta( $this->post_id, 'test_single', true ) ); + + $this->grant_write_permission(); + + $data = array( + 'meta' => array( + 'test_single' => 'test_value', + ), + ); + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', $this->post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + + $meta = get_post_meta( $this->post_id, 'test_single', true ); + $this->assertNotEmpty( $meta ); + $this->assertEquals( 'test_value', $meta ); + + $data = $response->get_data(); + $meta = (array) $data['meta']; + $this->assertArrayHasKey( 'test_single', $meta ); + $this->assertEquals( 'test_value', $meta['test_single'] ); + } + /** * @depends test_set_value */ From 96d9cf4a467ffc8e4159d23be4e2978b2ec29227 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Tue, 11 Oct 2016 20:46:44 -0500 Subject: [PATCH 214/318] Fix extra space --- lib/fields/class-wp-rest-meta-fields.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index ed9569bc2b..aaa5edb613 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -234,7 +234,7 @@ protected function update_meta_value( $object, $name, $value ) { } } - if ( ! update_metadata( $meta_type, $object, $meta_key, $meta_value ) ) { + if ( ! update_metadata( $meta_type, $object, $meta_key, $meta_value ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), From dd956b0bc7f74784276e22c7c1099ca13aa5a4b7 Mon Sep 17 00:00:00 2001 From: websupporter Date: Wed, 12 Oct 2016 09:47:00 +0300 Subject: [PATCH 215/318] Change priority for create_initial_rest_routes --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index 3c82cede93..db35477eb3 100755 --- a/plugin.php +++ b/plugin.php @@ -130,7 +130,7 @@ add_filter( 'init', '_add_extra_api_post_type_arguments', 11 ); add_action( 'init', '_add_extra_api_taxonomy_arguments', 11 ); add_action( 'rest_api_init', 'rest_register_settings', 0 ); -add_action( 'rest_api_init', 'create_initial_rest_routes', 0 ); +add_action( 'rest_api_init', 'create_initial_rest_routes', 99 ); /** * Adds extra post type registration arguments. From 8aecd9556bd33f923c5cf5ff589044caf0faccba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Wed, 12 Oct 2016 13:36:33 +0200 Subject: [PATCH 216/318] Missing full-stop in error message --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index d456bfcc67..4825063d9e 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -296,7 +296,7 @@ public function create_item_permissions_check( $request ) { } if ( empty( $request['post'] ) && ! current_user_can( 'moderate_comments' ) ) { - return new WP_Error( 'rest_comment_invalid_post_id', __( 'Sorry, you cannot create this comment without a post' ), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( 'rest_comment_invalid_post_id', __( 'Sorry, you cannot create this comment without a post.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( ! empty( $request['post'] ) && $post = $this->get_post( (int) $request['post'] ) ) { From c51eee5d59a63c6008b5dacb2c700134e853677b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Wed, 12 Oct 2016 13:52:58 +0200 Subject: [PATCH 217/318] Update class-wp-rest-posts-controller.php --- lib/endpoints/class-wp-rest-posts-controller.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 1f564bba6d..ccbac1f5ef 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -77,7 +77,7 @@ public function get_items_permissions_check( $request ) { $post_type = get_post_type_object( $this->post_type ); if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) { - return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit these posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; @@ -271,7 +271,7 @@ public function get_item_permissions_check( $request ) { $post = $this->get_post( (int) $request['id'] ); if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) { - return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post' ), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( $post && ! empty( $request['password'] ) ) { @@ -948,13 +948,13 @@ protected function handle_status_param( $post_status, $post_type ) { break; case 'private': if ( ! current_user_can( $post_type->cap->publish_posts ) ) { - return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create private posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to create private posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) ); } break; case 'publish': case 'future': if ( ! current_user_can( $post_type->cap->publish_posts ) ) { - return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type' ), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( 'rest_cannot_publish', __( 'Sorry, you are not allowed to publish posts in this post type.' ), array( 'status' => rest_authorization_required_code() ) ); } break; default: @@ -1917,6 +1917,6 @@ public function validate_user_can_query_private_statuses( $value, $request, $par if ( current_user_can( $post_type_obj->cap->edit_posts ) ) { return true; } - return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden' ), array( 'status' => rest_authorization_required_code() ) ); + return new WP_Error( 'rest_forbidden_status', __( 'Status is forbidden.' ), array( 'status' => rest_authorization_required_code() ) ); } } From 1edb0b8166e19929d186c53a72eb3979bc17dbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Wed, 12 Oct 2016 13:55:31 +0200 Subject: [PATCH 218/318] Update class-wp-rest-users-controller.php --- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 255f31fcc4..93864ad991 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -401,7 +401,7 @@ public function update_item( $request ) { } if ( ! empty( $request['username'] ) && $request['username'] !== $user->user_login ) { - return new WP_Error( 'rest_user_invalid_argument', __( "Username isn't editable" ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_user_invalid_argument', __( "Username isn't editable." ), array( 'status' => 400 ) ); } if ( ! empty( $request['slug'] ) && $request['slug'] !== $user->user_nicename && get_user_by( 'slug', $request['slug'] ) ) { From fcc440940c36b860c603f589ab9b1b26aaf87f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Wed, 12 Oct 2016 14:00:27 +0200 Subject: [PATCH 219/318] Update plugin.php --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index 3c82cede93..8478c16921 100755 --- a/plugin.php +++ b/plugin.php @@ -226,7 +226,7 @@ function rest_register_settings() { ), ), 'type' => 'string', - 'description' => __( 'Site URL' ), + 'description' => __( 'Site URL.' ), ) ); register_setting( 'general', 'admin_email', array( From 40afddd55e53e2c824c1ae5fe752132e655d7832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Wed, 12 Oct 2016 14:11:30 +0200 Subject: [PATCH 220/318] Update class-wp-rest-attachments-controller.php --- .../class-wp-rest-attachments-controller.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index df7718bb66..03153937b4 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -372,15 +372,15 @@ public function get_item_schema() { */ protected function upload_from_data( $data, $headers ) { if ( empty( $data ) ) { - return new WP_Error( 'rest_upload_no_data', __( 'No data supplied' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_upload_no_data', __( 'No data supplied.' ), array( 'status' => 400 ) ); } if ( empty( $headers['content_type'] ) ) { - return new WP_Error( 'rest_upload_no_content_type', __( 'No Content-Type supplied' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_upload_no_content_type', __( 'No Content-Type supplied.' ), array( 'status' => 400 ) ); } if ( empty( $headers['content_disposition'] ) ) { - return new WP_Error( 'rest_upload_no_content_disposition', __( 'No Content-Disposition supplied' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_upload_no_content_disposition', __( 'No Content-Disposition supplied.' ), array( 'status' => 400 ) ); } $filename = $this->get_filename_from_disposition( $headers['content_disposition'] ); @@ -395,7 +395,7 @@ protected function upload_from_data( $data, $headers ) { $actual = md5( $data ); if ( $expected !== $actual ) { - return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected' ), array( 'status' => 412 ) ); + return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected.' ), array( 'status' => 412 ) ); } } @@ -411,7 +411,7 @@ protected function upload_from_data( $data, $headers ) { $fp = fopen( $tmpfname, 'w+' ); if ( ! $fp ) { - return new WP_Error( 'rest_upload_file_error', __( 'Could not open file handle' ), array( 'status' => 500 ) ); + return new WP_Error( 'rest_upload_file_error', __( 'Could not open file handle.' ), array( 'status' => 500 ) ); } fwrite( $fp, $data ); @@ -553,7 +553,7 @@ public function validate_user_can_query_private_statuses( $value, $request, $par */ protected function upload_from_file( $files, $headers ) { if ( empty( $files ) ) { - return new WP_Error( 'rest_upload_no_data', __( 'No data supplied' ), array( 'status' => 400 ) ); + return new WP_Error( 'rest_upload_no_data', __( 'No data supplied.' ), array( 'status' => 400 ) ); } // Verify hash, if given @@ -562,7 +562,7 @@ protected function upload_from_file( $files, $headers ) { $expected = trim( $content_md5 ); $actual = md5_file( $files['file']['tmp_name'] ); if ( $expected !== $actual ) { - return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected' ), array( 'status' => 412 ) ); + return new WP_Error( 'rest_upload_hash_mismatch', __( 'Content hash did not match expected.' ), array( 'status' => 412 ) ); } } From 568e674a4c6ec12e3b803e5c5307ff95eaaf7005 Mon Sep 17 00:00:00 2001 From: mayukojpn Date: Wed, 12 Oct 2016 23:48:08 +0900 Subject: [PATCH 221/318] Use argument swapping for multiple placeholders. --- plugin.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugin.php b/plugin.php index 3c82cede93..8daf602f30 100755 --- a/plugin.php +++ b/plugin.php @@ -496,32 +496,32 @@ function rest_validate_request_arg( $value, $request, $param ) { if ( in_array( $args['type'], array( 'numeric', 'integer' ) ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) { if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be greater than %d (exclusive)' ), $param, $args['minimum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) ); } else if ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be greater than %d (inclusive)' ), $param, $args['minimum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (inclusive)' ), $param, $args['minimum'] ) ); } } else if ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be less than %d (exclusive)' ), $param, $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (exclusive)' ), $param, $args['maximum'] ) ); } else if ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be less than %d (inclusive)' ), $param, $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (inclusive)' ), $param, $args['maximum'] ) ); } } else if ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (exclusive) and %d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } } else if ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (inclusive) and %d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } } else if ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (exclusive) and %d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } } else if ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be between %d (inclusive) and %d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } } } From 261e909e2a87c80de0f841835ba24cd6f8c9e101 Mon Sep 17 00:00:00 2001 From: mayukojpn Date: Thu, 13 Oct 2016 00:01:06 +0900 Subject: [PATCH 222/318] Add comments for translators. --- plugin.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugin.php b/plugin.php index 8daf602f30..fdfa238022 100755 --- a/plugin.php +++ b/plugin.php @@ -496,32 +496,32 @@ function rest_validate_request_arg( $value, $request, $param ) { if ( in_array( $args['type'], array( 'numeric', 'integer' ) ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) { if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number */ __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) ); } else if ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (inclusive)' ), $param, $args['minimum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number */ __( '%1$s must be greater than %2$d (inclusive)' ), $param, $args['minimum'] ) ); } } else if ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (exclusive)' ), $param, $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: maximum number */ __( '%1$s must be less than %2$d (exclusive)' ), $param, $args['maximum'] ) ); } else if ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (inclusive)' ), $param, $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: maximum number */ __( '%1$s must be less than %2$d (inclusive)' ), $param, $args['maximum'] ) ); } } else if ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } } else if ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } } else if ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } } else if ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } } } From 2bbde403ce4c1d1ad22344946ec0b8fe48f8d303 Mon Sep 17 00:00:00 2001 From: mayukojpn Date: Thu, 13 Oct 2016 08:34:05 +0900 Subject: [PATCH 223/318] Word capitalization. --- lib/endpoints/class-wp-rest-attachments-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index df7718bb66..c174c080ae 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -337,7 +337,7 @@ public function get_item_schema() { 'readonly' => true, ); $schema['properties']['mime_type'] = array( - 'description' => __( 'Mime type of resource.' ), + 'description' => __( 'MIME type of resource.' ), 'type' => 'string', 'context' => array( 'view', 'edit', 'embed' ), 'readonly' => true, @@ -523,7 +523,7 @@ public function get_collection_params() { ); $params['mime_type'] = array( 'default' => null, - 'description' => __( 'Limit result set to attachments of a particular mime type.' ), + 'description' => __( 'Limit result set to attachments of a particular MIME type.' ), 'type' => 'string', ); return $params; @@ -588,7 +588,7 @@ protected function upload_from_file( $files, $headers ) { /** * Get the supported media types. - * Media types are considered the mime type category + * Media types are considered the MIME type category * * @return array */ From 549172ba290bc3ec0b25f949f5b69fd295fa7a1c Mon Sep 17 00:00:00 2001 From: miya0001 Date: Thu, 13 Oct 2016 11:52:41 +0900 Subject: [PATCH 224/318] remove ununsed variable $post_type_obj --- lib/endpoints/class-wp-rest-attachments-controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index c174c080ae..ad81e8c0cf 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -40,7 +40,6 @@ public function create_item_permissions_check( $request ) { return $ret; } - $post_type_obj = get_post_type_object( $this->post_type ); if ( ! current_user_can( 'upload_files' ) ) { return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to upload media on this site.' ), array( 'status' => 400 ) ); } From f754a2acbe6f1cef1be1e825754b32110133a61f Mon Sep 17 00:00:00 2001 From: mayukojpn Date: Thu, 13 Oct 2016 13:07:59 +0900 Subject: [PATCH 225/318] remove translator descriptions. --- plugin.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin.php b/plugin.php index fdfa238022..4b8b37307b 100755 --- a/plugin.php +++ b/plugin.php @@ -496,15 +496,15 @@ function rest_validate_request_arg( $value, $request, $param ) { if ( in_array( $args['type'], array( 'numeric', 'integer' ) ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) { if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number */ __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) ); } else if ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number */ __( '%1$s must be greater than %2$d (inclusive)' ), $param, $args['minimum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (inclusive)' ), $param, $args['minimum'] ) ); } } else if ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: maximum number */ __( '%1$s must be less than %2$d (exclusive)' ), $param, $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (exclusive)' ), $param, $args['maximum'] ) ); } else if ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: maximum number */ __( '%1$s must be less than %2$d (inclusive)' ), $param, $args['maximum'] ) ); + return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (inclusive)' ), $param, $args['maximum'] ) ); } } else if ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { From 77bb991a881fc52aea7fc1fd6f2c031ce0381a4b Mon Sep 17 00:00:00 2001 From: mayukojpn Date: Thu, 13 Oct 2016 13:30:40 +0900 Subject: [PATCH 226/318] Add more argument swapping and comments. --- plugin.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin.php b/plugin.php index 3c82cede93..b4655606ba 100755 --- a/plugin.php +++ b/plugin.php @@ -456,20 +456,20 @@ function rest_validate_request_arg( $value, $request, $param ) { if ( ! empty( $args['enum'] ) ) { if ( ! in_array( $value, $args['enum'] ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not one of %s' ), $param, implode( ', ', $args['enum'] ) ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: list of enum */ __( '%1$s is not one of %2$s' ), $param, implode( ', ', $args['enum'] ) ) ); } } if ( 'integer' === $args['type'] && ! is_numeric( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $param, 'integer' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s' ), $param, 'integer' ) ); } if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $value, 'boolean' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s' ), $value, 'boolean' ) ); } if ( 'string' === $args['type'] && ! is_string( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not of type %s' ), $param, 'string' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s' ), $param, 'string' ) ); } if ( isset( $args['format'] ) ) { From cac72aeffcfc039e985167a7de68f79f54dd7e84 Mon Sep 17 00:00:00 2001 From: miya0001 Date: Thu, 13 Oct 2016 13:37:23 +0900 Subject: [PATCH 227/318] fix $scheme is always empty --- lib/endpoints/class-wp-rest-terms-controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 394c983ffe..eac688bf5a 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -380,6 +380,7 @@ public function create_item( $request ) { */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, true ); + $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { $meta_update = $this->meta->update_value( $request['meta'], (int) $request['id'] ); if ( is_wp_error( $meta_update ) ) { From 23f598ccea31e9242b701bb687dac9dd7e9d1a5a Mon Sep 17 00:00:00 2001 From: mayukojpn Date: Thu, 13 Oct 2016 13:49:50 +0900 Subject: [PATCH 228/318] Period missing. --- plugin.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin.php b/plugin.php index b4655606ba..24e1577872 100755 --- a/plugin.php +++ b/plugin.php @@ -456,20 +456,20 @@ function rest_validate_request_arg( $value, $request, $param ) { if ( ! empty( $args['enum'] ) ) { if ( ! in_array( $value, $args['enum'] ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: list of enum */ __( '%1$s is not one of %2$s' ), $param, implode( ', ', $args['enum'] ) ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: list of enum */ __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) ); } } if ( 'integer' === $args['type'] && ! is_numeric( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s' ), $param, 'integer' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s.' ), $param, 'integer' ) ); } if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s' ), $value, 'boolean' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s.' ), $value, 'boolean' ) ); } if ( 'string' === $args['type'] && ! is_string( $value ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s' ), $param, 'string' ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: type name */ __( '%1$s is not of type %2$s.' ), $param, 'string' ) ); } if ( isset( $args['format'] ) ) { From 2b2498b4888707f3894fec8e6d15fefdd63fd7a6 Mon Sep 17 00:00:00 2001 From: miya0001 Date: Thu, 13 Oct 2016 14:50:35 +0900 Subject: [PATCH 229/318] remove ununsed variable $use_cache --- lib/endpoints/class-wp-rest-terms-controller.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 394c983ffe..7eac52a91d 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -146,14 +146,6 @@ public function get_items( $request ) { */ $prepared_args = apply_filters( "rest_{$this->taxonomy}_query", $prepared_args, $request ); - // Can we use the cached call? - $use_cache = ! empty( $prepared_args['post'] ) - && empty( $prepared_args['include'] ) - && empty( $prepared_args['exclude'] ) - && empty( $prepared_args['hide_empty'] ) - && empty( $prepared_args['search'] ) - && empty( $prepared_args['slug'] ); - if ( ! empty( $prepared_args['post'] ) ) { $query_result = $this->get_terms_for_post( $prepared_args ); $total_terms = $this->total_terms; From 4bf02a27104a5ccd2c01264a9b219c3707b05105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Fri, 14 Oct 2016 03:42:16 +0200 Subject: [PATCH 230/318] Add Parameter Comments for check_role_update Add Parameter Comments for check_role_update --- lib/endpoints/class-wp-rest-users-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 255f31fcc4..5a35efb78b 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -705,8 +705,8 @@ protected function prepare_item_for_database( $request ) { /** * Determine if the current user is allowed to make the desired roles change. * - * @param integer $user_id - * @param array $roles + * @param integer $user_id User ID. + * @param array $roles New user roles. * @return WP_Error|boolean */ protected function check_role_update( $user_id, $roles ) { From 1efbc5515fe494e784753f7019390e126f98e0cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Fri, 14 Oct 2016 04:07:52 +0200 Subject: [PATCH 231/318] Remove double semicolon Remove double semicolon --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 1f564bba6d..58f90138d8 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -470,7 +470,7 @@ public function update_item_permissions_check( $request ) { $post_type = get_post_type_object( $this->post_type ); if ( $post && ! $this->check_update_permission( $post ) ) { - return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to update this post.' ), array( 'status' => rest_authorization_required_code() ) );; + return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to update this post.' ), array( 'status' => rest_authorization_required_code() ) ); } if ( ! empty( $request['author'] ) && get_current_user_id() !== $request['author'] && ! current_user_can( $post_type->cap->edit_others_posts ) ) { From 202f7dc6ba7b085d04c918a19d3c6dc35d0b5100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Fri, 14 Oct 2016 04:44:21 +0200 Subject: [PATCH 232/318] Use Yoda Condition checks, you must Use Yoda Condition checks, you must `$post->post_type === 'attachment'` in delete_item() in class-wp-rest-posts-controller.php --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 1f564bba6d..7f6b6b5579 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -596,7 +596,7 @@ public function delete_item( $request ) { } $supports_trash = ( EMPTY_TRASH_DAYS > 0 ); - if ( $post->post_type === 'attachment' ) { + if ( 'attachment' === $post->post_type ) { $supports_trash = $supports_trash && MEDIA_TRASH; } From 38d38c68392c7a33e8904cd7bd83c5e2c5e47d92 Mon Sep 17 00:00:00 2001 From: mayukojpn Date: Fri, 14 Oct 2016 12:19:25 +0900 Subject: [PATCH 233/318] Update translator description. --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index 24e1577872..0f9dd709a3 100755 --- a/plugin.php +++ b/plugin.php @@ -456,7 +456,7 @@ function rest_validate_request_arg( $value, $request, $param ) { if ( ! empty( $args['enum'] ) ) { if ( ! in_array( $value, $args['enum'] ) ) { - return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: list of enum */ __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) ); + return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: list of valid values */ __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) ); } } From 78ef255755391e238ee86dc31f43c2a5cc37b1ae Mon Sep 17 00:00:00 2001 From: Toby Schrapel Date: Thu, 13 Oct 2016 21:06:59 +0100 Subject: [PATCH 234/318] Pass WP_REST_Request to get_allowed_query_vars and containing filters for #2821 --- lib/endpoints/class-wp-rest-posts-controller.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 1f564bba6d..6f4dda4ac2 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -662,7 +662,7 @@ public function delete_item( $request ) { */ protected function prepare_items_query( $prepared_args = array(), $request = null ) { - $valid_vars = array_flip( $this->get_allowed_query_vars() ); + $valid_vars = array_flip( $this->get_allowed_query_vars( $request ) ); $query_args = array(); foreach ( $valid_vars as $var => $index ) { if ( isset( $prepared_args[ $var ] ) ) { @@ -692,9 +692,10 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul /** * Get all the WP Query vars that are allowed for the API request. * + * @param WP_REST_Request $request * @return array */ - protected function get_allowed_query_vars() { + protected function get_allowed_query_vars( $request = null ) { global $wp; /** @@ -752,9 +753,10 @@ protected function get_allowed_query_vars() { * Array of allowed WP_Query query vars. * * @param string $allowed_query_var The query var to allow. + * @param WP_REST_Request $request Request object. * } */ - $valid_vars = apply_filters( 'rest_query_vars', $valid_vars ); + $valid_vars = apply_filters( 'rest_query_vars', $valid_vars, $request ); return $valid_vars; } From 4bc74ec1f7ec5d23aaf09876181809b1ed782ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Fri, 14 Oct 2016 14:01:35 +0200 Subject: [PATCH 235/318] Improve WP_REST_Revisions_Controller comments --- lib/endpoints/class-wp-rest-revisions-controller.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index d7f54319da..5db1211d62 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -148,7 +148,7 @@ public function delete_item_permissions_check( $request ) { /** * Delete a single revision * - * @param WP_REST_Request $request Full details about the request + * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ public function delete_item( $request ) { @@ -174,7 +174,7 @@ public function delete_item( $request ) { /** * Prepare the revision for the REST response * - * @param WP_Post $post Post revision object. + * @param WP_Post $post Post revision object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $response */ @@ -272,8 +272,8 @@ public function prepare_item_for_response( $post, $request ) { * Check the post_date_gmt or modified_gmt and prepare any post or * modified date for single post output. * - * @param string $date_gmt - * @param string|null $date + * @param string $date_gmt GMT publication time. + * @param string|null $date Optional, default is null. Local publication time. * @return string|null ISO8601/RFC3339 formatted datetime. */ protected function prepare_date_response( $date_gmt, $date = null ) { @@ -386,7 +386,8 @@ public function get_collection_params() { /** * Check the post excerpt and prepare it for single post output. * - * @param string $excerpt + * @param string $excerpt The post excerpt. + * @param int|WP_Post $post Post revision object. * @return string|null $excerpt */ protected function prepare_excerpt_response( $excerpt, $post ) { @@ -400,5 +401,4 @@ protected function prepare_excerpt_response( $excerpt, $post ) { return $excerpt; } - } From 46eacc7baa62bc00115045d94373f38be15786ba Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Fri, 14 Oct 2016 14:20:18 -0400 Subject: [PATCH 236/318] Do not run revisions excerpts through get_the_excerpt Running revisions excerpts through `'get_the_excerpt'` leads to `get_the_content` being called in such a way that `$post` does not get set. This causes ``` Notice: Trying to get property of non-object in /srv/www/wordpress-develop/src/wp-includes/post-template.php on line 298 ``` to be printed out before the JSON, which prevents the JSON being properly interpreted by requesting clients, e.g. `$.get`. --- lib/endpoints/class-wp-rest-revisions-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index d7f54319da..d21acabe64 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -392,7 +392,7 @@ public function get_collection_params() { protected function prepare_excerpt_response( $excerpt, $post ) { /** This filter is documented in wp-includes/post-template.php */ - $excerpt = apply_filters( 'the_excerpt', apply_filters( 'get_the_excerpt', $excerpt, $post ) ); + $excerpt = apply_filters( 'the_excerpt', $excerpt, $post ); if ( empty( $excerpt ) ) { return ''; From 92b33c9f1afdda954d2bfd1968d358628f43a691 Mon Sep 17 00:00:00 2001 From: websupporter Date: Fri, 14 Oct 2016 23:05:49 +0300 Subject: [PATCH 237/318] default priority for rest_register_settings --- plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.php b/plugin.php index db35477eb3..52bc78a8bc 100755 --- a/plugin.php +++ b/plugin.php @@ -129,7 +129,7 @@ add_filter( 'init', '_add_extra_api_post_type_arguments', 11 ); add_action( 'init', '_add_extra_api_taxonomy_arguments', 11 ); -add_action( 'rest_api_init', 'rest_register_settings', 0 ); +add_action( 'rest_api_init', 'rest_register_settings', 10 ); add_action( 'rest_api_init', 'create_initial_rest_routes', 99 ); /** From 7507003f845c5c72efa0679993d1021f08a1de0f Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sat, 15 Oct 2016 08:56:49 -0500 Subject: [PATCH 238/318] Add test for returning an error on invalid author param --- tests/test-rest-comments-controller.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 3577ec5ad5..6866ed5221 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -1152,6 +1152,23 @@ public function test_create_comment_require_login() { $this->assertEquals( 'rest_comment_login_required', $data['code'] ); } + public function test_create_item_invalid_author() { + wp_set_current_user( $this->admin_id ); + + $params = array( + 'post' => $this->post_id, + 'author' => REST_TESTS_IMPOSSIBLY_HIGH_NUMBER, + 'content' => "It\'s all over\, people! We don\'t have a prayer!", + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_comment_author_invalid', $response, 400 ); + } + public function test_create_comment_two_times() { global $wp_version; if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { From c60aa694df1d5cc3cef03b4c7a18c84bccf7a34a Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 11 Oct 2016 16:22:43 -0400 Subject: [PATCH 239/318] Fix typo in comment explaining orderby relevance error state --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 0906e4754c..9a76068aaf 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -91,7 +91,7 @@ public function get_items_permissions_check( $request ) { */ public function get_items( $request ) { - //Make sure a search string is set in case the orderby is set to 'relevace' + // Make sure a search string is set in case the orderby is set to 'relevance'. if ( ! empty( $request['orderby'] ) && 'relevance' === $request['orderby'] && empty( $request['search'] ) && empty( $request['filter']['s'] ) ) { return new WP_Error( 'rest_no_search_term_defined', __( 'You need to define a search term to order by relevance.' ), array( 'status' => 400 ) ); } From 3f1355ce395262da4fc05558d2a17132fb3949a1 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 11 Oct 2016 16:22:57 -0400 Subject: [PATCH 240/318] Extend parameter whitelisting behavior to terms controller --- .../class-wp-rest-terms-controller.php | 42 +++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 1a423c9686..f7d793d191 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -101,19 +101,37 @@ public function get_items_permissions_check( $request ) { * @return WP_REST_Response|WP_Error */ public function get_items( $request ) { - $prepared_args = array( - 'exclude' => $request['exclude'], - 'include' => $request['include'], - 'order' => $request['order'], - 'orderby' => $request['orderby'], - 'post' => $request['post'], - 'hide_empty' => $request['hide_empty'], - 'number' => $request['per_page'], - 'search' => $request['search'], - 'slug' => $request['slug'], + + // Retrieve the list of registered collection query parameters. + $registered = $this->get_collection_params(); + + // This array defines mappings between public API query parameters whose + // values are accepted as-passed, and their internal WP_Query parameter + // name equivalents (some are the same). Only values which are also + // present in $registered will be set. + $parameter_mappings = array( + 'exclude' => 'exclude', + 'include' => 'include', + 'order' => 'order', + 'orderby' => 'orderby', + 'post' => 'post', + 'hide_empty' => 'hide_empty', + 'per_page' => 'number', + 'search' => 'search', + 'slug' => 'slug', ); - if ( ! empty( $request['offset'] ) ) { + $prepared_args = array(); + + // For each known parameter which is both registered and present in the request, + // set the parameter's value on the query $prepared_args. + foreach ( $parameter_mappings as $api_param => $wp_param ) { + if ( isset( $registered[ $api_param ] ) && isset( $request[ $api_param ] ) ) { + $prepared_args[ $wp_param ] = $request[ $api_param ]; + } + } + + if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) { $prepared_args['offset'] = $request['offset']; } else { $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; @@ -121,7 +139,7 @@ public function get_items( $request ) { $taxonomy_obj = get_taxonomy( $this->taxonomy ); - if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) { + if ( $taxonomy_obj->hierarchical && isset( $registered['parent'] ) && isset( $request['parent'] ) ) { if ( 0 === $request['parent'] ) { // Only query top-level terms. $prepared_args['parent'] = 0; From 4349846b15b93a5acf2d60870a2c8bc7d79c3f71 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 11 Oct 2016 16:57:48 -0400 Subject: [PATCH 241/318] Extend param whitelisting behavior to users & taxonomies controllers --- .../class-wp-rest-taxonomies-controller.php | 6 +- .../class-wp-rest-users-controller.php | 62 +++++++++++++------ 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/lib/endpoints/class-wp-rest-taxonomies-controller.php b/lib/endpoints/class-wp-rest-taxonomies-controller.php index d6eaafb67f..76a441b5ec 100755 --- a/lib/endpoints/class-wp-rest-taxonomies-controller.php +++ b/lib/endpoints/class-wp-rest-taxonomies-controller.php @@ -65,7 +65,11 @@ public function get_items_permissions_check( $request ) { * @return array */ public function get_items( $request ) { - if ( ! empty( $request['type'] ) ) { + + // Retrieve the list of registered collection query parameters. + $registered = $this->get_collection_params(); + + if ( isset( $registered['type'] ) && ! empty( $request['type'] ) ) { $taxonomies = get_object_taxonomies( $request['type'], 'objects' ); } else { $taxonomies = get_taxonomies( '', 'objects' ); diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 20d4ff3645..22e062d50f 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -103,28 +103,50 @@ public function get_items_permissions_check( $request ) { */ public function get_items( $request ) { + // Retrieve the list of registered collection query parameters. + $registered = $this->get_collection_params(); + + // This array defines mappings between public API query parameters whose + // values are accepted as-passed, and their internal WP_Query parameter + // name equivalents (some are the same). Only values which are also + // present in $registered will be set. + $parameter_mappings = array( + 'exclude' => 'exclude', + 'include' => 'include', + 'order' => 'order', + 'per_page' => 'number', + 'search' => 'search', + 'roles' => 'role__in', + ); + $prepared_args = array(); - $prepared_args['exclude'] = $request['exclude']; - $prepared_args['include'] = $request['include']; - $prepared_args['order'] = $request['order']; - $prepared_args['number'] = $request['per_page']; - if ( ! empty( $request['offset'] ) ) { + + // For each known parameter which is both registered and present in the request, + // set the parameter's value on the query $prepared_args. + foreach ( $parameter_mappings as $api_param => $wp_param ) { + if ( isset( $registered[ $api_param ] ) && isset( $request[ $api_param ] ) ) { + $prepared_args[ $wp_param ] = $request[ $api_param ]; + } + } + + if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) { $prepared_args['offset'] = $request['offset']; } else { - $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; - } - $orderby_possibles = array( - 'id' => 'ID', - 'include' => 'include', - 'name' => 'display_name', - 'registered_date' => 'registered', - 'slug' => 'user_nicename', - 'email' => 'user_email', - 'url' => 'user_url', - ); - $prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ]; - $prepared_args['search'] = $request['search']; - $prepared_args['role__in'] = $request['roles']; + $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; + } + + if ( isset( $registered['orderby'] ) ) { + $orderby_possibles = array( + 'id' => 'ID', + 'include' => 'include', + 'name' => 'display_name', + 'registered_date' => 'registered', + 'slug' => 'user_nicename', + 'email' => 'user_email', + 'url' => 'user_url', + ); + $prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ]; + } if ( ! current_user_can( 'list_users' ) ) { $prepared_args['has_published_posts'] = true; @@ -134,7 +156,7 @@ public function get_items( $request ) { $prepared_args['search'] = '*' . $prepared_args['search'] . '*'; } - if ( ! empty( $request['slug'] ) ) { + if ( isset( $registered['slug'] ) && ! empty( $request['slug'] ) ) { $prepared_args['search'] = $request['slug']; $prepared_args['search_columns'] = array( 'user_nicename' ); } From 394a151fea192582794601d9e3c8e4cf483ce0bd Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 11 Oct 2016 17:27:13 -0400 Subject: [PATCH 242/318] Extend parameter whitelisting behavior to comments controller --- .../class-wp-rest-comments-controller.php | 74 +++++++++++++------ 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 7b947ca69b..e999849d70 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -117,43 +117,69 @@ public function get_items_permissions_check( $request ) { * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { - $prepared_args = array( - 'author_email' => isset( $request['author_email'] ) ? $request['author_email'] : '', - 'comment__in' => $request['include'], - 'comment__not_in' => $request['exclude'], - 'karma' => isset( $request['karma'] ) ? $request['karma'] : '', - 'number' => $request['per_page'], - 'post__in' => $request['post'], - 'parent__in' => $request['parent'], - 'parent__not_in' => $request['parent_exclude'], - 'search' => $request['search'], - 'offset' => $request['offset'], - 'orderby' => $this->normalize_query_param( $request['orderby'] ), - 'order' => $request['order'], - 'status' => $request['status'], - 'type' => $request['type'], - 'no_found_rows' => false, - 'author__in' => $request['author'], - 'author__not_in' => $request['author_exclude'], + + // Retrieve the list of registered collection query parameters. + $registered = $this->get_collection_params(); + + // This array defines mappings between public API query parameters whose + // values are accepted as-passed, and their internal WP_Query parameter + // name equivalents (some are the same). Only values which are also + // present in $registered will be set. + $parameter_mappings = array( + 'author' => 'author__in', + 'author_email' => 'author_email', + 'author_exclude' => 'author__not_in', + 'exclude' => 'comment__not_in', + 'include' => 'comment__in', + 'karma' => 'karma', + 'offset' => 'offset', + 'order' => 'order', + 'parent' => 'parent__in', + 'parent_exclude' => 'parent__not_in', + 'per_page' => 'number', + 'post' => 'post__in', + 'search' => 'search', + 'status' => 'status', + 'type' => 'type', ); + $prepared_args = array(); + + // For each known parameter which is both registered and present in the request, + // set the parameter's value on the query $prepared_args. + foreach ( $parameter_mappings as $api_param => $wp_param ) { + if ( isset( $registered[ $api_param ] ) && isset( $request[ $api_param ] ) ) { + $prepared_args[ $wp_param ] = $request[ $api_param ]; + } + } + + // Ensure certain parameter values default to empty strings. + foreach ( array( 'author_email', 'karma', 'search' ) as $param ) { + if ( ! isset( $prepared_args[ $param ] ) ) { + $prepared_args[ $param ] = ''; + } + } + + if ( isset( $registered['orderby'] ) ) { + $prepared_args['orderby'] = $this->normalize_query_param( $request['orderby'] ); + } + + $prepared_args['no_found_rows'] = false; + $prepared_args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. - if ( isset( $request['before'] ) ) { + if ( isset( $registered['before'] ) && isset( $request['before'] ) ) { $prepared_args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. - if ( isset( $request['after'] ) ) { + if ( isset( $registered['after'] ) && isset( $request['after'] ) ) { $prepared_args['date_query'][0]['after'] = $request['after']; } - if ( empty( $request['offset'] ) ) { + if ( isset( $registered['page'] ) && empty( $request['offset'] ) ) { $prepared_args['offset'] = $prepared_args['number'] * ( absint( $request['page'] ) - 1 ); } - if ( empty( $request['search'] ) ) { - $prepared_args['search'] = ''; - } /** * Filter arguments, before passing to WP_Comment_Query, when querying comments via the REST API. From 240406d2718115c5324c591701093ea319cde2ae Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sat, 15 Oct 2016 17:02:47 -0500 Subject: [PATCH 243/318] Remove schema property that is setting the comment author name to an empty string by default --- lib/endpoints/class-wp-rest-comments-controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index e1ad3f0cae..073e30a6ac 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -909,7 +909,6 @@ public function get_item_schema() { 'context' => array( 'view', 'edit', 'embed' ), 'arg_options' => array( 'sanitize_callback' => 'sanitize_text_field', - 'default' => '', ), ), 'author_url' => array( From 65f0647e395aa145f961ab40216632ba81b5ee1c Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Sat, 15 Oct 2016 17:03:38 -0500 Subject: [PATCH 244/318] Add unit tests for overriding the comment author info AND pulling it from the WP_User object --- tests/test-rest-comments-controller.php | 37 +++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 6866ed5221..d4e44d0625 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -29,7 +29,12 @@ public function setUp() { 'role' => 'subscriber', )); $this->author_id = $this->factory->user->create( array( - 'role' => 'author', + 'role' => 'author', + 'display_name' => 'Sea Captain', + 'first_name' => 'Horatio', + 'last_name' => 'McCallister', + 'user_email' => 'captain@thefryingdutchman.com', + 'user_url' => 'http://thefryingdutchman.com', )); $this->post_id = $this->factory->post->create(); @@ -914,7 +919,7 @@ public function test_create_comment_other_user() { 'author_email' => 'chunkylover53@aol.com', 'author_url' => 'http://compuglobalhypermeganet.com', 'content' => 'Here\’s to alcohol: the cause of, and solution to, all of life\’s problems.', - 'author' => 0, + 'author' => $this->subscriber_id, ); $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); @@ -924,7 +929,10 @@ public function test_create_comment_other_user() { $this->assertEquals( 201, $response->get_status() ); $data = $response->get_data(); - $this->assertEquals( 0, $data['author'] ); + $this->assertEquals( $this->subscriber_id, $data['author'] ); + $this->assertEquals( 'Homer Jay Simpson', $data['author_name'] ); + $this->assertEquals( 'chunkylover53@aol.com', $data['author_email'] ); + $this->assertEquals( 'http://compuglobalhypermeganet.com', $data['author_url'] ); } public function test_create_comment_other_user_without_permission() { @@ -1169,6 +1177,29 @@ public function test_create_item_invalid_author() { $this->assertErrorResponse( 'rest_comment_author_invalid', $response, 400 ); } + public function test_create_item_pull_author_info() { + wp_set_current_user( $this->admin_id ); + + $author = new WP_User( $this->author_id ); + $params = array( + 'post' => $this->post_id, + 'author' => $this->author_id, + 'content' => "It\'s all over\, people! We don\'t have a prayer!", + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + + $result = $response->get_data(); + $this->assertSame( $this->author_id, $result['author'] ); + $this->assertSame( 'Sea Captain', $result['author_name'] ); + $this->assertSame( 'captain@thefryingdutchman.com', $result['author_email'] ); + $this->assertSame( 'http://thefryingdutchman.com', $result['author_url'] ); + } + public function test_create_comment_two_times() { global $wp_version; if ( version_compare( $wp_version, '4.7-alpha', '<' ) ) { From e70cb8845604907e3343ae52e840116d40b718b4 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Sun, 16 Oct 2016 12:30:25 +1100 Subject: [PATCH 245/318] Use `in_array` strict comparisons. --- .../class-wp-rest-attachments-controller.php | 12 ++++++------ lib/endpoints/class-wp-rest-controller.php | 4 ++-- .../class-wp-rest-posts-controller.php | 18 +++++++++--------- .../class-wp-rest-terms-controller.php | 2 +- plugin.php | 2 +- ...test-rest-post-type-controller-testcase.php | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index c6368aa09b..3b7a798f77 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -12,16 +12,16 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { */ protected function prepare_items_query( $prepared_args = array(), $request = null ) { $query_args = parent::prepare_items_query( $prepared_args, $request ); - if ( empty( $query_args['post_status'] ) || ! in_array( $query_args['post_status'], array( 'inherit', 'private', 'trash' ) ) ) { + if ( empty( $query_args['post_status'] ) || ! in_array( $query_args['post_status'], array( 'inherit', 'private', 'trash' ), true ) ) { $query_args['post_status'] = 'inherit'; } $media_types = $this->get_media_types(); - if ( ! empty( $request['media_type'] ) && in_array( $request['media_type'], array_keys( $media_types ) ) ) { + if ( ! empty( $request['media_type'] ) && in_array( $request['media_type'], array_keys( $media_types ), true ) ) { $query_args['post_mime_type'] = $media_types[ $request['media_type'] ]; } if ( ! empty( $request['mime_type'] ) ) { $parts = explode( '/', $request['mime_type'] ); - if ( isset( $media_types[ $parts[0] ] ) && in_array( $request['mime_type'], $media_types[ $parts[0] ] ) ) { + if ( isset( $media_types[ $parts[0] ] ) && in_array( $request['mime_type'], $media_types[ $parts[0] ] ), true ) { $query_args['post_mime_type'] = $request['mime_type']; } } @@ -64,7 +64,7 @@ public function create_item_permissions_check( $request ) { */ public function create_item( $request ) { - if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ) ) ) { + if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ), true ) ) { return new WP_Error( 'rest_invalid_param', __( 'Invalid parent type.' ), array( 'status' => 400 ) ); } @@ -114,7 +114,7 @@ public function create_item( $request ) { $id = wp_insert_post( $attachment, true ); if ( is_wp_error( $id ) ) { - if ( in_array( $id->get_error_code(), array( 'db_update_error' ) ) ) { + if ( in_array( $id->get_error_code(), array( 'db_update_error' ), true ) ) { $id->add_data( array( 'status' => 500 ) ); } else { $id->add_data( array( 'status' => 400 ) ); @@ -163,7 +163,7 @@ public function create_item( $request ) { * @return WP_Error|WP_REST_Response */ public function update_item( $request ) { - if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ) ) ) { + if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ), true ) ) { return new WP_Error( 'rest_invalid_param', __( 'Invalid parent type.' ), array( 'status' => 400 ) ); } $response = parent::update_item( $request ); diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 564addba72..221266e999 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -187,7 +187,7 @@ public function filter_response_by_context( $data, $context ) { continue; } - if ( ! in_array( $context, $schema['properties'][ $key ]['context'] ) ) { + if ( ! in_array( $context, $schema['properties'][ $key ]['context'], true ) ) { unset( $data[ $key ] ); continue; } @@ -197,7 +197,7 @@ public function filter_response_by_context( $data, $context ) { if ( empty( $details['context'] ) ) { continue; } - if ( ! in_array( $context, $details['context'] ) ) { + if ( ! in_array( $context, $details['context'], true ) ) { if ( isset( $data[ $key ][ $attribute ] ) ) { unset( $data[ $key ][ $attribute ] ); } diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 9a76068aaf..e730e45109 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -413,7 +413,7 @@ public function create_item( $request ) { if ( is_wp_error( $post_id ) ) { - if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ) ) ) { + if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ), true ) ) { $post_id->add_data( array( 'status' => 500 ) ); } else { $post_id->add_data( array( 'status' => 400 ) ); @@ -526,7 +526,7 @@ public function update_item( $request ) { // convert the post object to an array, otherwise wp_update_post will expect non-escaped input $post_id = wp_update_post( (array) $post, true ); if ( is_wp_error( $post_id ) ) { - if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ) ) ) { + if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ), true ) ) { $post_id->add_data( array( 'status' => 500 ) ); } else { $post_id->add_data( array( 'status' => 400 ) ); @@ -1021,7 +1021,7 @@ protected function handle_featured_media( $featured_media, $post_id ) { * @param integer $post_id */ public function handle_template( $template, $post_id ) { - if ( in_array( $template, array_keys( wp_get_theme()->get_page_templates( $this->get_post( $post_id ) ) ) ) ) { + if ( in_array( $template, array_keys( wp_get_theme()->get_page_templates( $this->get_post( $post_id ) ) ), true ) ) { update_post_meta( $post_id, '_wp_page_template', $template ); } else { update_post_meta( $post_id, '_wp_page_template', '' ); @@ -1375,7 +1375,7 @@ protected function prepare_links( $post ) { ), ); - if ( ( in_array( $post->post_type, array( 'post', 'page' ) ) || post_type_supports( $post->post_type, 'author' ) ) + if ( ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'author' ) ) && ! empty( $post->post_author ) ) { $links['author'] = array( 'href' => rest_url( 'wp/v2/users/' . $post->post_author ), @@ -1383,7 +1383,7 @@ protected function prepare_links( $post ) { ); }; - if ( in_array( $post->post_type, array( 'post', 'page' ) ) || post_type_supports( $post->post_type, 'comments' ) ) { + if ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'comments' ) ) { $replies_url = rest_url( 'wp/v2/comments' ); $replies_url = add_query_arg( 'post', $post->ID, $replies_url ); $links['replies'] = array( @@ -1392,7 +1392,7 @@ protected function prepare_links( $post ) { ); } - if ( in_array( $post->post_type, array( 'post', 'page' ) ) || post_type_supports( $post->post_type, 'revisions' ) ) { + if ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'revisions' ) ) { $links['version-history'] = array( 'href' => rest_url( trailingslashit( $base ) . $post->ID . '/revisions' ), ); @@ -1413,7 +1413,7 @@ protected function prepare_links( $post ) { 'embeddable' => true, ); } - if ( ! in_array( $post->post_type, array( 'attachment', 'nav_menu_item', 'revision' ) ) ) { + if ( ! in_array( $post->post_type, array( 'attachment', 'nav_menu_item', 'revision' ), true ) ) { $attachments_url = rest_url( 'wp/v2/media' ); $attachments_url = add_query_arg( 'parent', $post->ID, $attachments_url ); $links['https://api.w.org/attachment'] = array( @@ -1600,9 +1600,9 @@ public function get_item_schema() { ), ); foreach ( $post_type_attributes as $attribute ) { - if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ] ) ) { + if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ], true ) ) { continue; - } elseif ( ! in_array( $this->post_type, array_keys( $fixed_schemas ) ) && ! post_type_supports( $this->post_type, $attribute ) ) { + } elseif ( ! in_array( $this->post_type, array_keys( $fixed_schemas ), true ) && ! post_type_supports( $this->post_type, $attribute ) ) { continue; } diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index f7d793d191..7f91b31a56 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -236,7 +236,7 @@ protected function get_terms_for_post( $prepared_args ) { * get_items() verifies that we don't have `include` set, and default * ordering is by `name`. */ - if ( ! in_array( $prepared_args['orderby'], array( 'name', 'none', 'include' ) ) ) { + if ( ! in_array( $prepared_args['orderby'], array( 'name', 'none', 'include' ), true ) ) { switch ( $prepared_args['orderby'] ) { case 'id': $this->sort_column = 'term_id'; diff --git a/plugin.php b/plugin.php index cbd0634a28..3bfd400a73 100755 --- a/plugin.php +++ b/plugin.php @@ -493,7 +493,7 @@ function rest_validate_request_arg( $value, $request, $param ) { } } - if ( in_array( $args['type'], array( 'numeric', 'integer' ) ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) { + if ( in_array( $args['type'], array( 'numeric', 'integer' ), true ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) { if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) ); diff --git a/tests/class-wp-test-rest-post-type-controller-testcase.php b/tests/class-wp-test-rest-post-type-controller-testcase.php index aa41af235a..5f49125ae8 100644 --- a/tests/class-wp-test-rest-post-type-controller-testcase.php +++ b/tests/class-wp-test-rest-post-type-controller-testcase.php @@ -187,7 +187,7 @@ protected function check_post_data( $post, $data, $context, $links ) { $this->assertEquals( $links['up'][0]['href'], rest_url( 'wp/v2/' . $post_type->rest_base . '/' . $data['parent'] ) ); } - if ( ! in_array( $data['type'], array( 'attachment', 'nav_menu_item', 'revision' ) ) ) { + if ( ! in_array( $data['type'], array( 'attachment', 'nav_menu_item', 'revision' ), true ) ) { $this->assertEquals( $links['https://api.w.org/attachment'][0]['href'], add_query_arg( 'parent', $data['id'], rest_url( 'wp/v2/media' ) ) ); } From 37a52e600f9b725f2bca359c066af04c79cc78e6 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Sun, 16 Oct 2016 12:46:34 +1100 Subject: [PATCH 246/318] Fix syntax error. --- lib/endpoints/class-wp-rest-attachments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index 3b7a798f77..a816a1b139 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -21,7 +21,7 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul } if ( ! empty( $request['mime_type'] ) ) { $parts = explode( '/', $request['mime_type'] ); - if ( isset( $media_types[ $parts[0] ] ) && in_array( $request['mime_type'], $media_types[ $parts[0] ] ), true ) { + if ( isset( $media_types[ $parts[0] ] ) && in_array( $request['mime_type'], $media_types[ $parts[0] ], true ) ) { $query_args['post_mime_type'] = $request['mime_type']; } } From 1a99202fc21fa803c48f1e06746cf12f4a66919b Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Sun, 16 Oct 2016 14:02:00 +1100 Subject: [PATCH 247/318] Use `elseif` consistently. --- .../class-wp-rest-comments-controller.php | 6 +++--- lib/endpoints/class-wp-rest-users-controller.php | 2 +- plugin.php | 14 +++++++------- tests/bootstrap.php | 8 ++++---- tests/test-rest-posts-controller.php | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index e999849d70..34ad90c85a 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -76,7 +76,7 @@ public function get_items_permissions_check( $request ) { $post = $this->get_post( $post_id ); if ( ! empty( $post_id ) && $post && ! $this->check_read_post_permission( $post ) ) { return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); - } else if ( 0 === $post_id && ! current_user_can( 'moderate_comments' ) ) { + } elseif ( 0 === $post_id && ! current_user_can( 'moderate_comments' ) ) { return new WP_Error( 'rest_cannot_read', __( 'Sorry, you cannot read comments without a post.' ), array( 'status' => rest_authorization_required_code() ) ); } } @@ -94,11 +94,11 @@ public function get_items_permissions_check( $request ) { if ( 'approve' !== $request[ $param ] ) { $forbidden_params[] = $param; } - } else if ( 'type' === $param ) { + } elseif ( 'type' === $param ) { if ( 'comment' !== $request[ $param ] ) { $forbidden_params[] = $param; } - } else if ( ! empty( $request[ $param ] ) ) { + } elseif ( ! empty( $request[ $param ] ) ) { $forbidden_params[] = $param; } } diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 22e062d50f..92d71616a4 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -239,7 +239,7 @@ public function get_item_permissions_check( $request ) { if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this resource with edit context.' ), array( 'status' => rest_authorization_required_code() ) ); - } else if ( ! count_user_posts( $id, $types ) && ! current_user_can( 'edit_user', $id ) && ! current_user_can( 'list_users' ) ) { + } elseif ( ! count_user_posts( $id, $types ) && ! current_user_can( 'edit_user', $id ) && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you cannot view this resource.' ), array( 'status' => rest_authorization_required_code() ) ); } diff --git a/plugin.php b/plugin.php index cbd0634a28..9a18aa39aa 100755 --- a/plugin.php +++ b/plugin.php @@ -497,29 +497,29 @@ function rest_validate_request_arg( $value, $request, $param ) { if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (exclusive)' ), $param, $args['minimum'] ) ); - } else if ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { + } elseif ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d (inclusive)' ), $param, $args['minimum'] ) ); } - } else if ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { + } elseif ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (exclusive)' ), $param, $args['maximum'] ) ); - } else if ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { + } elseif ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d (inclusive)' ), $param, $args['maximum'] ) ); } - } else if ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { + } elseif ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } - } else if ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { + } elseif ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { if ( $value >= $args['maximum'] || $value < $args['minimum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } - } else if ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { + } elseif ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value <= $args['minimum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } - } else if ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { + } elseif ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { if ( $value > $args['maximum'] || $value < $args['minimum'] ) { return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: minimum number, 3: maximum number */ __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ce85a480e4..d1bd4fa3b4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -19,13 +19,13 @@ */ if ( false !== getenv( 'WP_DEVELOP_DIR' ) ) { $test_root = getenv( 'WP_DEVELOP_DIR' ) . '/tests/phpunit'; -} else if ( false !== getenv( 'WP_TESTS_DIR' ) ) { +} elseif ( false !== getenv( 'WP_TESTS_DIR' ) ) { $test_root = getenv( 'WP_TESTS_DIR' ); -} else if ( false !== getenv( 'WP_ROOT_DIR' ) ) { +} elseif ( false !== getenv( 'WP_ROOT_DIR' ) ) { $test_root = getenv( 'WP_ROOT_DIR' ) . '/tests/phpunit'; -} else if ( file_exists( '../../../../tests/phpunit/includes/bootstrap.php' ) ) { +} elseif ( file_exists( '../../../../tests/phpunit/includes/bootstrap.php' ) ) { $test_root = '../../../../tests/phpunit'; -} else if ( file_exists( '/tmp/wordpress-tests-lib/includes/bootstrap.php' ) ) { +} elseif ( file_exists( '/tmp/wordpress-tests-lib/includes/bootstrap.php' ) ) { $test_root = '/tmp/wordpress-tests-lib'; } diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 66832ed612..5e8a61804d 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -666,7 +666,7 @@ public function test_get_item_links() { $tag_link = $link; } elseif ( 'category' === $link['attributes']['taxonomy'] ) { $cat_link = $link; - } else if ( 'post_format' === $link['attributes']['taxonomy'] ) { + } elseif ( 'post_format' === $link['attributes']['taxonomy'] ) { $format_link = $link; } } From 185608ff086d7587f42278e7df0a902e49ff425f Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Sun, 16 Oct 2016 14:21:49 -0400 Subject: [PATCH 248/318] Return empty taxonomy collections as JSON object, not array The response from `/wp/v2/taxonomies` is a JSON object, keyed with the name of each taxonomy. If you use the `?type=` query parameter to specify an object for which no taxonomies are available, or to specify an invalid type of object, the empty PHP `array()` will be rendered in JSON as `[]`. To ensure that the type of this resource's response remains consistent this pull request casts empty arrays to a PHP object, which will be correctly rendered as `{}` in the response JSON. Fixes #2803 --- lib/endpoints/class-wp-rest-taxonomies-controller.php | 6 ++++++ tests/test-rest-taxonomies-controller.php | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-taxonomies-controller.php b/lib/endpoints/class-wp-rest-taxonomies-controller.php index 76a441b5ec..010186ed8b 100755 --- a/lib/endpoints/class-wp-rest-taxonomies-controller.php +++ b/lib/endpoints/class-wp-rest-taxonomies-controller.php @@ -83,6 +83,12 @@ public function get_items( $request ) { $tax = $this->prepare_response_for_collection( $tax ); $data[ $tax_type ] = $tax; } + + if ( empty( $data ) ) { + // Response should still be returned as a JSON object when it is empty. + $data = (object) $data; + } + return rest_ensure_response( $data ); } diff --git a/tests/test-rest-taxonomies-controller.php b/tests/test-rest-taxonomies-controller.php index e34fea4e47..585f47a136 100644 --- a/tests/test-rest-taxonomies-controller.php +++ b/tests/test-rest-taxonomies-controller.php @@ -46,13 +46,22 @@ public function test_get_items_invalid_permission_for_context() { $this->assertErrorResponse( 'rest_cannot_view', $response, 401 ); } - public function test_get_taxonomies_with_types() { + public function test_get_taxonomies_for_type() { $request = new WP_REST_Request( 'GET', '/wp/v2/taxonomies' ); $request->set_param( 'type', 'post' ); $response = $this->server->dispatch( $request ); $this->check_taxonomies_for_type_response( 'post', $response ); } + public function test_get_taxonomies_for_invalid_type() { + $request = new WP_REST_Request( 'GET', '/wp/v2/taxonomies' ); + $request->set_param( 'type', 'wingding' ); + $response = $this->server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + $data = $response->get_data(); + $this->assertEquals( json_encode( $data ), '{}' ); + } + public function test_get_item() { $request = new WP_REST_Request( 'GET', '/wp/v2/taxonomies/category' ); $response = $this->server->dispatch( $request ); From 56c9183f4ec9d46c955684ed06865764344756c8 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Sun, 16 Oct 2016 14:26:48 -0400 Subject: [PATCH 249/318] Reverse order of arguments in assertEquals call for consistency --- tests/test-rest-taxonomies-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-taxonomies-controller.php b/tests/test-rest-taxonomies-controller.php index 585f47a136..9cf9522f36 100644 --- a/tests/test-rest-taxonomies-controller.php +++ b/tests/test-rest-taxonomies-controller.php @@ -59,7 +59,7 @@ public function test_get_taxonomies_for_invalid_type() { $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); - $this->assertEquals( json_encode( $data ), '{}' ); + $this->assertEquals( '{}', json_encode( $data ) ); } public function test_get_item() { From e25d95b8a6312d76708f518f666bf1d7d934fcc6 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Mon, 17 Oct 2016 09:51:16 -0500 Subject: [PATCH 250/318] Fix unneeded double quotes around slashed comment content in tests --- tests/test-rest-comments-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index d4e44d0625..3726ab1a17 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -802,7 +802,7 @@ public function test_create_item_invalid_date() { 'author_name' => 'Reverend Lovejoy', 'author_email' => 'lovejoy@example.com', 'author_url' => 'http://timothylovejoy.jr', - 'content' => "It\'s all over\, people! We don\'t have a prayer!", + 'content' => 'It\'s all over\, people! We don\'t have a prayer!', 'date' => rand_str(), ); @@ -1166,7 +1166,7 @@ public function test_create_item_invalid_author() { $params = array( 'post' => $this->post_id, 'author' => REST_TESTS_IMPOSSIBLY_HIGH_NUMBER, - 'content' => "It\'s all over\, people! We don\'t have a prayer!", + 'content' => 'It\'s all over\, people! We don\'t have a prayer!', ); $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); @@ -1184,7 +1184,7 @@ public function test_create_item_pull_author_info() { $params = array( 'post' => $this->post_id, 'author' => $this->author_id, - 'content' => "It\'s all over\, people! We don\'t have a prayer!", + 'content' => 'It\'s all over\, people! We don\'t have a prayer!', ); $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); From e7f0220396a50f05234b7767b42c04007e7987c1 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Mon, 17 Oct 2016 10:22:23 -0500 Subject: [PATCH 251/318] Fix the inline documentation in WP_REST_Meta_Fields to match core standards - Added inline doc comments for missing @params - Normalized the inconsistent `$object` and `$id` params within the class methods to be `$object_id` - Added missing periods at the end of comments or descriptions. - Adjusted spacing to line up the type, variable and descriptions of the @param docs. --- lib/fields/class-wp-rest-meta-fields.php | 85 ++++++++++++------------ 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index aaa5edb613..41aed0b2a6 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -3,19 +3,20 @@ /** * Manage meta values for an object. */ - abstract class WP_REST_Meta_Fields { + /** * Get the object type for meta. * - * @return string One of 'post', 'comment', 'term', 'user', or anything else supported by `_get_meta_table()` + * @return string One of 'post', 'comment', 'term', 'user', or anything + * else supported by `_get_meta_table()`. */ abstract protected function get_meta_type(); /** * Get the object type for `register_rest_field`. * - * @return string Custom post type, 'taxonomy', 'comment', or `user` + * @return string Custom post type, 'taxonomy', 'comment', or `user`. */ abstract protected function get_rest_field_type(); @@ -33,16 +34,16 @@ public function register_field() { /** * Get the `meta` field value. * - * @param int $id Object ID to fetch meta for. - * @param WP_REST_Request $request Full details about the request. + * @param int $object_id Object ID to fetch meta for. + * @param WP_REST_Request $request Full details about the request. * @return WP_Error|array */ - public function get_value( $id, $request ) { + public function get_value( $object_id, $request ) { $fields = $this->get_registered_fields(); $response = array(); foreach ( $fields as $name => $args ) { - $all_values = get_metadata( $this->get_meta_type(), $id, $name, false ); + $all_values = get_metadata( $this->get_meta_type(), $object_id, $name, false ); if ( $args['single'] ) { if ( empty( $all_values ) ) { $value = $args['schema']['default']; @@ -67,12 +68,12 @@ public function get_value( $id, $request ) { * Prepare value for response. * * This is required because some native types cannot be stored correctly in - * the database, such as booleans. We need to cast back to the relevant type - * before passing back to JSON. + * the database, such as booleans. We need to cast back to the relevant + * type before passing back to JSON. * - * @param mixed $value Value to prepare. + * @param mixed $value Value to prepare. * @param WP_REST_Request $request Current request object. - * @param array $args Options for the field. + * @param array $args Options for the field. * @return mixed Prepared value. */ protected function prepare_value_for_response( $value, $request, $args ) { @@ -86,25 +87,26 @@ protected function prepare_value_for_response( $value, $request, $args ) { /** * Update meta values. * - * @param WP_REST_Request $request Full detail about the request. + * @param WP_REST_Request $request Full details about the request. + * @param int $object_id Object ID to fetch meta for. * @return WP_Error|null Error if one occurs, null on success. */ - public function update_value( $params, $id ) { + public function update_value( $request, $object_id ) { $fields = $this->get_registered_fields(); foreach ( $fields as $name => $args ) { - if ( ! array_key_exists( $name, $params ) ) { + if ( ! array_key_exists( $name, $request ) ) { continue; } // A null value means reset the field, which is essentially deleting it // from the database and then relying on the default value. - if ( is_null( $params[ $name ] ) ) { - $result = $this->delete_meta_value( $id, $name ); + if ( is_null( $request[ $name ] ) ) { + $result = $this->delete_meta_value( $object_id, $name ); } elseif ( $args['single'] ) { - $result = $this->update_meta_value( $id, $name, $params[ $name ] ); + $result = $this->update_meta_value( $object_id, $name, $request[ $name ] ); } else { - $result = $this->update_multi_meta_value( $id, $name, $params[ $name ] ); + $result = $this->update_multi_meta_value( $object_id, $name, $request[ $name ] ); } if ( is_wp_error( $result ) ) { @@ -118,12 +120,12 @@ public function update_value( $params, $id ) { /** * Delete meta value for an object. * - * @param int $object Object ID the field belongs to. - * @param string $name Key for the field. + * @param int $object_id Object ID the field belongs to. + * @param string $name Key for the field. * @return bool|WP_Error True if meta field is deleted, error otherwise. */ - protected function delete_meta_value( $object, $name ) { - if ( ! current_user_can( 'delete_post_meta', $object, $name ) ) { + protected function delete_meta_value( $object_id, $name ) { + if ( ! current_user_can( 'delete_post_meta', $object_id, $name ) ) { return new WP_Error( 'rest_cannot_delete', sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ), @@ -131,7 +133,7 @@ protected function delete_meta_value( $object, $name ) { ); } - if ( ! delete_metadata( $this->get_meta_type(), $object, wp_slash( $name ) ) ) { + if ( ! delete_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ) ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not delete meta value from database.' ), @@ -147,13 +149,13 @@ protected function delete_meta_value( $object, $name ) { * * Alters the list of values in the database to match the list of provided values. * - * @param int $object Object ID. - * @param string $name Key for the custom field. - * @param array $values List of values to update to. + * @param int $object_id Object ID to update. + * @param string $name Key for the custom field. + * @param array $values List of values to update to. * @return bool|WP_Error True if meta fields are updated, error otherwise. */ - protected function update_multi_meta_value( $object, $name, $values ) { - if ( ! current_user_can( 'edit_post_meta', $object, $name ) ) { + protected function update_multi_meta_value( $object_id, $name, $values ) { + if ( ! current_user_can( 'edit_post_meta', $object_id, $name ) ) { return new WP_Error( 'rest_cannot_update', sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ), @@ -161,7 +163,7 @@ protected function update_multi_meta_value( $object, $name, $values ) { ); } - $current = get_metadata( $this->get_meta_type(), $object, $name, false ); + $current = get_metadata( $this->get_meta_type(), $object_id, $name, false ); $to_remove = $current; $to_add = $values; @@ -185,7 +187,7 @@ protected function update_multi_meta_value( $object, $name, $values ) { // once. $to_remove = array_unique( $to_remove ); foreach ( $to_remove as $value ) { - if ( ! delete_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { + if ( ! delete_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ), wp_slash( $value ) ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), @@ -194,7 +196,7 @@ protected function update_multi_meta_value( $object, $name, $values ) { } } foreach ( $to_add as $value ) { - if ( ! add_metadata( $this->get_meta_type(), $object, wp_slash( $name ), wp_slash( $value ) ) ) { + if ( ! add_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ), wp_slash( $value ) ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), @@ -209,12 +211,13 @@ protected function update_multi_meta_value( $object, $name, $values ) { /** * Update meta value for an object. * - * @param int $object Object ID. - * @param string $name Key for the custom field. + * @param int $object_id Object ID to update. + * @param string $name Key for the custom field. + * @param mixed $value Updated value. * @return bool|WP_Error True if meta field is updated, error otherwise. */ - protected function update_meta_value( $object, $name, $value ) { - if ( ! current_user_can( 'edit_post_meta', $object, $name ) ) { + protected function update_meta_value( $object_id, $name, $value ) { + if ( ! current_user_can( 'edit_post_meta', $object_id, $name ) ) { return new WP_Error( 'rest_cannot_update', sprintf( __( 'You do not have permission to edit the %s custom field.' ), $name ), @@ -227,14 +230,14 @@ protected function update_meta_value( $object, $name, $value ) { $meta_value = wp_slash( $value ); // Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false. - $old_value = get_metadata( $meta_type, $object, $meta_key ); + $old_value = get_metadata( $meta_type, $object_id, $meta_key ); if ( 1 === count( $old_value ) ) { if ( $old_value[0] === $meta_value ) { return true; } } - if ( ! update_metadata( $meta_type, $object, $meta_key, $meta_value ) ) { + if ( ! update_metadata( $meta_type, $object_id, $meta_key, $meta_value ) ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), @@ -278,7 +281,7 @@ protected function get_registered_fields() { $rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] ); if ( empty( $rest_args['schema']['type'] ) ) { - // Skip over meta fields that don't have a defined type + // Skip over meta fields that don't have a defined type. if ( empty( $args['type'] ) ) { continue; } @@ -294,7 +297,7 @@ protected function get_registered_fields() { } $registered[ $rest_args['name'] ] = $rest_args; - } + } // End foreach(). return $registered; } @@ -327,9 +330,9 @@ public function get_field_schema() { * Default preparation for meta fields. Override by passing the * `prepare_callback` in your `show_in_rest` options. * - * @param mixed $value Meta value from the database. + * @param mixed $value Meta value from the database. * @param WP_REST_Request $request Request object. - * @param array $args REST-specific options for the meta key. + * @param array $args REST-specific options for the meta key. * @return mixed Value prepared for output. */ public static function prepare_value( $value, $request, $args ) { From bc27397d4376b6e2a6291fe2140fa6091519bcf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Mon, 17 Oct 2016 20:11:13 +0200 Subject: [PATCH 252/318] Removes 'int' parameter docu. Adds 'access' --- .../class-wp-rest-revisions-controller.php | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index 5db1211d62..0a69fff449 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -17,6 +17,8 @@ public function __construct( $parent_post_type ) { /** * Register routes for revisions based on post types supporting revisions + * + * @access public */ public function register_routes() { @@ -51,6 +53,8 @@ public function register_routes() { /** * Check if a given request has access to get revisions + * + * @access public * * @param WP_REST_Request $request Full data about the request. * @return WP_Error|boolean @@ -71,6 +75,8 @@ public function get_items_permissions_check( $request ) { /** * Get a collection of revisions + * + * @access public * * @param WP_REST_Request $request Full data about the request. * @return WP_Error|WP_REST_Response @@ -94,6 +100,8 @@ public function get_items( $request ) { /** * Check if a given request has access to get a specific revision + * + * @access public * * @param WP_REST_Request $request Full data about the request. * @return WP_Error|boolean @@ -104,6 +112,8 @@ public function get_item_permissions_check( $request ) { /** * Get one revision from the collection + * + * @access public * * @param WP_REST_Request $request Full data about the request. * @return WP_Error|array @@ -126,6 +136,8 @@ public function get_item( $request ) { /** * Check if a given request has access to delete a revision + * + * @access public * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean @@ -147,6 +159,8 @@ public function delete_item_permissions_check( $request ) { /** * Delete a single revision + * + * @access public * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean @@ -173,6 +187,8 @@ public function delete_item( $request ) { /** * Prepare the revision for the REST response + * + * @access public * * @param WP_Post $post Post revision object. * @param WP_REST_Request $request Request object. @@ -271,6 +287,8 @@ public function prepare_item_for_response( $post, $request ) { /** * Check the post_date_gmt or modified_gmt and prepare any post or * modified date for single post output. + * + * @access protected * * @param string $date_gmt GMT publication time. * @param string|null $date Optional, default is null. Local publication time. @@ -290,6 +308,8 @@ protected function prepare_date_response( $date_gmt, $date = null ) { /** * Get the revision's schema, conforming to JSON Schema + * + * @access public * * @return array */ @@ -374,6 +394,8 @@ public function get_item_schema() { /** * Get the query params for collections + * + * @access public * * @return array */ @@ -385,9 +407,11 @@ public function get_collection_params() { /** * Check the post excerpt and prepare it for single post output. + * + * @access protected * - * @param string $excerpt The post excerpt. - * @param int|WP_Post $post Post revision object. + * @param string $excerpt The post excerpt. + * @param WP_Post $post Post revision object. * @return string|null $excerpt */ protected function prepare_excerpt_response( $excerpt, $post ) { From 07461cc6e3ae30ed0bc0472aed460503c79abbe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Mon, 17 Oct 2016 20:38:52 +0200 Subject: [PATCH 253/318] Update class-wp-rest-revisions-controller.php --- .../class-wp-rest-revisions-controller.php | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/endpoints/class-wp-rest-revisions-controller.php b/lib/endpoints/class-wp-rest-revisions-controller.php index 0a69fff449..f5f009fe5f 100755 --- a/lib/endpoints/class-wp-rest-revisions-controller.php +++ b/lib/endpoints/class-wp-rest-revisions-controller.php @@ -17,7 +17,7 @@ public function __construct( $parent_post_type ) { /** * Register routes for revisions based on post types supporting revisions - * + * * @access public */ public function register_routes() { @@ -53,7 +53,7 @@ public function register_routes() { /** * Check if a given request has access to get revisions - * + * * @access public * * @param WP_REST_Request $request Full data about the request. @@ -75,7 +75,7 @@ public function get_items_permissions_check( $request ) { /** * Get a collection of revisions - * + * * @access public * * @param WP_REST_Request $request Full data about the request. @@ -100,7 +100,7 @@ public function get_items( $request ) { /** * Check if a given request has access to get a specific revision - * + * * @access public * * @param WP_REST_Request $request Full data about the request. @@ -112,7 +112,7 @@ public function get_item_permissions_check( $request ) { /** * Get one revision from the collection - * + * * @access public * * @param WP_REST_Request $request Full data about the request. @@ -136,7 +136,7 @@ public function get_item( $request ) { /** * Check if a given request has access to delete a revision - * + * * @access public * * @param WP_REST_Request $request Full details about the request. @@ -159,7 +159,7 @@ public function delete_item_permissions_check( $request ) { /** * Delete a single revision - * + * * @access public * * @param WP_REST_Request $request Full details about the request. @@ -187,7 +187,7 @@ public function delete_item( $request ) { /** * Prepare the revision for the REST response - * + * * @access public * * @param WP_Post $post Post revision object. @@ -287,7 +287,7 @@ public function prepare_item_for_response( $post, $request ) { /** * Check the post_date_gmt or modified_gmt and prepare any post or * modified date for single post output. - * + * * @access protected * * @param string $date_gmt GMT publication time. @@ -308,7 +308,7 @@ protected function prepare_date_response( $date_gmt, $date = null ) { /** * Get the revision's schema, conforming to JSON Schema - * + * * @access public * * @return array @@ -394,7 +394,7 @@ public function get_item_schema() { /** * Get the query params for collections - * + * * @access public * * @return array @@ -407,7 +407,7 @@ public function get_collection_params() { /** * Check the post excerpt and prepare it for single post output. - * + * * @access protected * * @param string $excerpt The post excerpt. From 62d811138327d3d4d0b630b034944a74b6b9c2d6 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Mon, 17 Oct 2016 16:00:05 -0400 Subject: [PATCH 254/318] Remove "validate_callback" on arrays where it's not used. Currently we are specify teh `validate_callback` for the number-list style params that are passed to `wp_parse_id_list()`, however, the validate callback never does anything here, as our validate callback doesn't handle array. When we _do_ support arrays, we won't be able to use it here anyway, as these are "special" params that are not really arrays, but in stread comma seperated strings that are converted to arrays. --- lib/endpoints/class-wp-rest-comments-controller.php | 8 -------- lib/endpoints/class-wp-rest-posts-controller.php | 2 -- 2 files changed, 10 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index b587543913..ba3d1e9baa 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -1073,20 +1073,17 @@ public function get_collection_params() { 'description' => __( 'Limit result set to comments assigned to specific user ids. Requires authorization.' ), 'sanitize_callback' => 'wp_parse_id_list', 'type' => 'array', - 'validate_callback' => 'rest_validate_request_arg', ); $query_params['author_exclude'] = array( 'description' => __( 'Ensure result set excludes comments assigned to specific user ids. Requires authorization.' ), 'sanitize_callback' => 'wp_parse_id_list', 'type' => 'array', - 'validate_callback' => 'rest_validate_request_arg', ); $query_params['author_email'] = array( 'default' => null, 'description' => __( 'Limit result set to that from a specific author email. Requires authorization.' ), 'format' => 'email', 'sanitize_callback' => 'sanitize_email', - 'validate_callback' => 'rest_validate_request_arg', 'type' => 'string', ); $query_params['before'] = array( @@ -1100,14 +1097,12 @@ public function get_collection_params() { 'type' => 'array', 'default' => array(), 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', ); $query_params['include'] = array( 'description' => __( 'Limit result set to specific ids.' ), 'type' => 'array', 'default' => array(), 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', ); $query_params['karma'] = array( 'default' => null, @@ -1154,21 +1149,18 @@ public function get_collection_params() { 'description' => __( 'Limit result set to resources of specific parent ids.' ), 'sanitize_callback' => 'wp_parse_id_list', 'type' => 'array', - 'validate_callback' => 'rest_validate_request_arg', ); $query_params['parent_exclude'] = array( 'default' => array(), 'description' => __( 'Ensure result set excludes specific parent ids.' ), 'sanitize_callback' => 'wp_parse_id_list', 'type' => 'array', - 'validate_callback' => 'rest_validate_request_arg', ); $query_params['post'] = array( 'default' => array(), 'description' => __( 'Limit result set to resources assigned to specific post ids.' ), 'type' => 'array', 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', ); $query_params['status'] = array( 'default' => 'approve', diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index e730e45109..bbd8fa1374 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1801,14 +1801,12 @@ public function get_collection_params() { 'type' => 'array', 'default' => array(), 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', ); $params['author_exclude'] = array( 'description' => __( 'Ensure result set excludes posts assigned to specific authors.' ), 'type' => 'array', 'default' => array(), 'sanitize_callback' => 'wp_parse_id_list', - 'validate_callback' => 'rest_validate_request_arg', ); } $params['before'] = array( From 75d4a2d9c00e6d654d2e397bb576dad4b3f43bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Mon, 17 Oct 2016 23:20:12 +0200 Subject: [PATCH 255/318] Improves WP_REST_Posts_Controller documentation. --- .../class-wp-rest-posts-controller.php | 116 ++++++++++++++---- 1 file changed, 93 insertions(+), 23 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index bbd8fa1374..e41b12568b 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -2,8 +2,21 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { + /** + * Post type. + * + * @access protected + * @var string + */ protected $post_type; + /** + * Constructor. + * + * @access public + * + * @param string $post_type Post type. + */ public function __construct( $post_type ) { $this->post_type = $post_type; $this->namespace = 'wp/v2'; @@ -15,6 +28,8 @@ public function __construct( $post_type ) { /** * Register the routes for the objects of the controller. + * + * @access public */ public function register_routes() { @@ -69,6 +84,8 @@ public function register_routes() { /** * Check if a given request has access to read /posts. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ @@ -86,6 +103,8 @@ public function get_items_permissions_check( $request ) { /** * Get a collection of posts. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ @@ -243,7 +262,7 @@ public function get_items( $request ) { $total_posts = $posts_query->found_posts; if ( $total_posts < 1 ) { - // Out-of-bounds, run the query again without LIMIT for total count + // Out-of-bounds, run the query again without LIMIT for total count. unset( $query_args['paged'] ); $count_query = new WP_Query(); $count_query->query( $query_args ); @@ -284,6 +303,8 @@ public function get_items( $request ) { /** * Check if a given request has access to read a post. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ @@ -320,7 +341,9 @@ public function get_item_permissions_check( $request ) { * This method determines whether we need to override the regular password * check in core with a filter. * - * @param WP_Post $post Post to check against. + * @access protected + * + * @param WP_Post $post Post to check against. * @param WP_REST_Request $request Request data to check. * @return bool True if the user can access passworded content, false otherwise. */ @@ -347,6 +370,8 @@ protected function can_access_password_content( $post, $request ) { /** * Get a single post. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ @@ -371,6 +396,8 @@ public function get_item( $request ) { /** * Check if a given request has access to create a post. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ @@ -395,6 +422,8 @@ public function create_item_permissions_check( $request ) { /** * Create a single post. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ @@ -482,6 +511,8 @@ public function create_item( $request ) { /** * Check if a given request has access to update a post. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ @@ -508,6 +539,8 @@ public function update_item_permissions_check( $request ) { /** * Update a single post. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ @@ -523,7 +556,7 @@ public function update_item( $request ) { if ( is_wp_error( $post ) ) { return $post; } - // convert the post object to an array, otherwise wp_update_post will expect non-escaped input + // convert the post object to an array, otherwise wp_update_post will expect non-escaped input. $post_id = wp_update_post( (array) $post, true ); if ( is_wp_error( $post_id ) ) { if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ), true ) ) { @@ -586,6 +619,8 @@ public function update_item( $request ) { /** * Check if a given request has access to delete a post. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error */ @@ -603,6 +638,8 @@ public function delete_item_permissions_check( $request ) { /** * Delete a single post. * + * @access public + * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error */ @@ -677,8 +714,10 @@ public function delete_item( $request ) { * Determine the allowed query_vars for a get_items() response and * prepare for WP_Query. * - * @param array $prepared_args - * @param WP_REST_Request $request + * @access protected + * + * @param array $prepared_args Prepared WP_Query Arguments. + * @param WP_REST_Request $request Full details about the request. * @return array $query_args */ protected function prepare_items_query( $prepared_args = array(), $request = null ) { @@ -693,7 +732,6 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul * The dynamic portion of the hook name, $var, refers to the query_var key. * * @param mixed $prepared_args[ $var ] The query_var value. - * */ $query_args[ $var ] = apply_filters( "rest_query_var-{$var}", $prepared_args[ $var ] ); } @@ -713,7 +751,9 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul /** * Get all the WP Query vars that are allowed for the API request. * - * @param WP_REST_Request $request + * @access protected + * + * @param WP_REST_Request $request Full details about the request. * @return array */ protected function get_allowed_query_vars( $request = null ) { @@ -786,8 +826,8 @@ protected function get_allowed_query_vars( $request = null ) { * Check the post_date_gmt or modified_gmt and prepare any post or * modified date for single post output. * - * @param string $date_gmt - * @param string|null $date + * @param string $date_gmt GMT publication time. + * @param string|null $date Optional, default is null. Local publication time. * @return string|null ISO8601/RFC3339 formatted datetime. */ protected function prepare_date_response( $date_gmt, $date = null ) { @@ -808,6 +848,8 @@ protected function prepare_date_response( $date_gmt, $date = null ) { /** * Prepare a single post for create or update. * + * @access protected + * * @param WP_REST_Request $request Request object. * @return WP_Error|stdClass $prepared_post Post object. */ @@ -887,7 +929,7 @@ protected function prepare_item_for_database( $request ) { $prepared_post->post_name = $request['slug']; } - // Author + // Author. if ( ! empty( $schema['properties']['author'] ) && ! empty( $request['author'] ) ) { $post_author = (int) $request['author']; if ( get_current_user_id() !== $post_author ) { @@ -959,8 +1001,10 @@ protected function prepare_item_for_database( $request ) { /** * Determine validity and normalize provided status param. * - * @param string $post_status - * @param object $post_type + * @access protected + * + * @param string $post_status Post status. + * @param object $post_type Post type. * @return WP_Error|string $post_status */ protected function handle_status_param( $post_status, $post_type ) { @@ -993,9 +1037,10 @@ protected function handle_status_param( $post_status, $post_type ) { /** * Determine the featured media based on a request param. * - * @param int $featured_media - * @param int $post_id + * @access protected * + * @param int $featured_media Featured Media ID. + * @param int $post_id Post ID. * @return bool|WP_Error */ protected function handle_featured_media( $featured_media, $post_id ) { @@ -1017,8 +1062,10 @@ protected function handle_featured_media( $featured_media, $post_id ) { /** * Set the template for a page. * - * @param string $template - * @param integer $post_id + * @access public + * + * @param string $template Page template filename. + * @param integer $post_id Post ID. */ public function handle_template( $template, $post_id ) { if ( in_array( $template, array_keys( wp_get_theme()->get_page_templates( $this->get_post( $post_id ) ) ), true ) ) { @@ -1031,6 +1078,8 @@ public function handle_template( $template, $post_id ) { /** * Update the post's terms from a REST request. * + * @access protected + * * @param int $post_id The post ID to update the terms form. * @param WP_REST_Request $request The request object with post and terms data. * @return null|WP_Error WP_Error on an error assigning any of ther terms. @@ -1054,7 +1103,9 @@ protected function handle_terms( $post_id, $request ) { /** * Check if a given post type should be viewed or managed. * - * @param object|string $post_type + * @access protected + * + * @param object|string $post_type Post type name or object. * @return boolean Is post type allowed? */ protected function check_is_post_type_allowed( $post_type ) { @@ -1074,6 +1125,8 @@ protected function check_is_post_type_allowed( $post_type ) { * * Correctly handles posts with the inherit status. * + * @access public + * * @param object $post Post object. * @return boolean Can we read it? */ @@ -1111,6 +1164,8 @@ public function check_read_permission( $post ) { /** * Check if we can edit a post. * + * @access protected + * * @param object $post Post object. * @return boolean Can we edit it? */ @@ -1127,6 +1182,8 @@ protected function check_update_permission( $post ) { /** * Check if we can create a post. * + * @access protected + * * @param object $post Post object. * @return boolean Can we create it?. */ @@ -1143,6 +1200,8 @@ protected function check_create_permission( $post ) { /** * Check if we can delete a post. * + * @access protected + * * @param object $post Post object. * @return boolean Can we delete it? */ @@ -1159,7 +1218,9 @@ protected function check_delete_permission( $post ) { /** * Prepare a single post output for response. * - * @param WP_Post $post Post object. + * @access public + * + * @param WP_Post $post Post object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $data */ @@ -1346,7 +1407,8 @@ public function prepare_item_for_response( $post, $request ) { * "Protected: %s", as the REST API communicates the protected status of a post * in a machine readable format, we remove the "Protected: " prefix. * - * @param string $format + * @access public + * * @return string */ public function protected_title_format() { @@ -1356,13 +1418,15 @@ public function protected_title_format() { /** * Prepare links for the request. * + * @access protected + * * @param WP_Post $post Post object. * @return array Links for the given post. */ protected function prepare_links( $post ) { $base = sprintf( '%s/%s', $this->namespace, $this->rest_base ); - // Entity meta + // Entity meta. $links = array( 'self' => array( 'href' => rest_url( trailingslashit( $base ) . $post->ID ), @@ -1453,6 +1517,8 @@ protected function prepare_links( $post ) { /** * Get the Post's schema, conforming to JSON Schema. * + * @access public + * * @return array */ public function get_item_schema() { @@ -1782,6 +1848,8 @@ public function get_item_schema() { /** * Get the query params for collections of attachments. * + * @access public + * * @return array */ public function get_collection_params() { @@ -1923,10 +1991,12 @@ public function get_collection_params() { } /** - * Validate whether the user can query private statuses + * Validate whether the user can query private statuses. + * + * @access public * - * @param mixed $value - * @param WP_REST_Request $request + * @param mixed $value Post status. + * @param WP_REST_Request $request Full details about the request. * @param string $parameter * @return WP_Error|boolean */ From 38380f109a72bce3b15a5c766471e432b63a1a0e Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Tue, 18 Oct 2016 20:42:35 -0500 Subject: [PATCH 256/318] Remove comment content check that was already added in #2741 --- lib/endpoints/class-wp-rest-comments-controller.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index eb27d4105e..da198a1805 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -364,11 +364,6 @@ public function create_item( $request ) { return new WP_Error( 'rest_comment_content_invalid', __( 'Comment content is invalid.' ), array( 'status' => 400 ) ); } - // Check that comment has content. - if ( empty( $prepared_comment['comment_content'] ) ) { - return new WP_Error( 'rest_require_valid_comment', __( 'Comment content required.' ), array( 'status' => 400 ) ); - } - // Setting remaining values before wp_insert_comment so we can // use wp_allow_comment(). if ( ! isset( $prepared_comment['comment_date_gmt'] ) ) { From 8e6f204c93189b5774fe91de8deacbeff638c3c4 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Tue, 18 Oct 2016 20:47:39 -0500 Subject: [PATCH 257/318] Return different errors based on missing required values --- lib/endpoints/class-wp-rest-comments-controller.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index da198a1805..1e4f5c2bbe 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -410,9 +410,13 @@ public function create_item( $request ) { } // Check author name and email if required. - if ( get_option( 'require_name_email' ) && ! isset( $user ) ) { - if ( empty( $prepared_comment['comment_author_email'] ) || empty( $prepared_comment['comment_author'] ) ) { - return new WP_Error( 'rest_require_valid_comment', __( 'Required fields (name, email) missing.' ), array( 'status' => 400 ) ); + if ( get_option( 'require_name_email' ) && ! isset( $prepared_comment['comment_author'] ) ) { + if ( ! isset( $prepared_comment['comment_author'] ) && ! isset( $prepared_comment['comment_author_email'] ) ) { + return new WP_Error( 'rest_comment_author_data_required', __( 'Creating a comment requires valid author name and email values.' ), array( 'status' => 400 ) ); + } elseif ( ! isset( $prepared_comment['comment_author'] ) ) { + return new WP_Error( 'rest_comment_author_required', __( 'Creating a comment requires a valid author name.' ), array( 'status' => 400 ) ); + } elseif ( ! isset( $prepared_comment['comment_author_email'] ) ) { + return new WP_Error( 'rest_comment_author_email_required', __( 'Creating a comment requires a valid author email.' ), array( 'status' => 400 ) ); } } From 503c05f07fb8ffd56b6e77536521f98d8cf38716 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Tue, 18 Oct 2016 20:49:17 -0500 Subject: [PATCH 258/318] Simplify the discussion setting conditional --- lib/endpoints/class-wp-rest-comments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 1e4f5c2bbe..bd5c8fa860 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -410,7 +410,7 @@ public function create_item( $request ) { } // Check author name and email if required. - if ( get_option( 'require_name_email' ) && ! isset( $prepared_comment['comment_author'] ) ) { + if ( get_option( 'require_name_email' ) ) { if ( ! isset( $prepared_comment['comment_author'] ) && ! isset( $prepared_comment['comment_author_email'] ) ) { return new WP_Error( 'rest_comment_author_data_required', __( 'Creating a comment requires valid author name and email values.' ), array( 'status' => 400 ) ); } elseif ( ! isset( $prepared_comment['comment_author'] ) ) { From 1e3f522e405570a95e5acd8f0565e15d7fdc1f9d Mon Sep 17 00:00:00 2001 From: Jon Desrosiers Date: Tue, 18 Oct 2016 22:18:18 -0400 Subject: [PATCH 259/318] Do not allow comments on trashed or draft posts. --- lib/endpoints/class-wp-rest-comments-controller.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index ba3d1e9baa..9a8575a6eb 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -326,6 +326,13 @@ public function create_item_permissions_check( $request ) { } if ( ! empty( $request['post'] ) && $post = $this->get_post( (int) $request['post'] ) ) { + if ( 'draft' === $post->post_status ) { + return new WP_Error( 'rest_comment_draft_post', __( 'Sorry, you cannot create a comment on this post.' ), array( 'status' => 403 ) ); + } + + if ( 'trash' === $post->post_status ) { + return new WP_Error( 'rest_comment_trash_post', __( 'Sorry, you cannot create a comment on this post.' ), array( 'status' => 403 ) ); + } if ( ! $this->check_read_post_permission( $post ) ) { return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you cannot read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); From 5bbea9a7682cd676bdd0c12873b2c3319516dd98 Mon Sep 17 00:00:00 2001 From: Jon Desrosiers Date: Tue, 18 Oct 2016 22:19:32 -0400 Subject: [PATCH 260/318] Add unit tests. Fix unit test typo. --- tests/test-rest-comments-controller.php | 50 ++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 3726ab1a17..ca397e363d 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -13,6 +13,8 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase protected $post_id; protected $private_id; + protected $draft_id; + protected $trash_id; protected $approved_id; protected $hold_id; @@ -41,6 +43,12 @@ public function setUp() { $this->private_id = $this->factory->post->create( array( 'post_status' => 'private', )); + $this->draft_id = $this->factory->post->create( array( + 'post_status' => 'draft', + )); + $this->trash_id = $this->factory->post->create( array( + 'post_status' => 'trash', + )); $this->approved_id = $this->factory->comment->create( array( 'comment_approved' => 1, @@ -1080,7 +1088,47 @@ public function test_create_comment_no_post_id_no_permission() { $this->assertErrorResponse( 'rest_comment_invalid_post_id', $response, 403 ); } - public function test_create_comment_private_post_invalide_permission() { + public function test_create_comment_draft_post() { + wp_set_current_user( $this->subscriber_id ); + + $params = array( + 'post' => $this->draft_id, + 'author_name' => 'Ishmael', + 'author_email' => 'herman-melville@earthlink.net', + 'author_url' => 'https://en.wikipedia.org/wiki/Herman_Melville', + 'content' => 'Call me Ishmael.', + 'author' => $this->subscriber_id, + ); + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_comment_draft_post', $response, 403 ); + } + + public function test_create_comment_trash_post() { + wp_set_current_user( $this->subscriber_id ); + + $params = array( + 'post' => $this->trash_id, + 'author_name' => 'Ishmael', + 'author_email' => 'herman-melville@earthlink.net', + 'author_url' => 'https://en.wikipedia.org/wiki/Herman_Melville', + 'content' => 'Call me Ishmael.', + 'author' => $this->subscriber_id, + ); + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_comment_trash_post', $response, 403 ); + } + + public function test_create_comment_private_post_invalid_permission() { wp_set_current_user( $this->subscriber_id ); $params = array( From 7fc05ca759a9349826572e61543ed8b457ac263c Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Tue, 18 Oct 2016 21:42:29 -0500 Subject: [PATCH 261/318] Add unit tests for errors of missing comment author name or email --- .../class-wp-rest-comments-controller.php | 22 ++++---- tests/test-rest-comments-controller.php | 56 +++++++++++++++++++ 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index bd5c8fa860..73b1da8135 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -384,6 +384,17 @@ public function create_item( $request ) { $prepared_comment['comment_author_url'] = $user->user_url; } + // Check author name and email if required. + if ( get_option( 'require_name_email' ) ) { + if ( ! isset( $prepared_comment['comment_author'] ) && ! isset( $prepared_comment['comment_author_email'] ) ) { + return new WP_Error( 'rest_comment_author_data_required', __( 'Creating a comment requires valid author name and email values.' ), array( 'status' => 400 ) ); + } elseif ( ! isset( $prepared_comment['comment_author'] ) ) { + return new WP_Error( 'rest_comment_author_required', __( 'Creating a comment requires a valid author name.' ), array( 'status' => 400 ) ); + } elseif ( ! isset( $prepared_comment['comment_author_email'] ) ) { + return new WP_Error( 'rest_comment_author_email_required', __( 'Creating a comment requires a valid author email.' ), array( 'status' => 400 ) ); + } + } + if ( ! isset( $prepared_comment['comment_author_email'] ) ) { $prepared_comment['comment_author_email'] = ''; } @@ -409,17 +420,6 @@ public function create_item( $request ) { return $prepared_comment['comment_approved']; } - // Check author name and email if required. - if ( get_option( 'require_name_email' ) ) { - if ( ! isset( $prepared_comment['comment_author'] ) && ! isset( $prepared_comment['comment_author_email'] ) ) { - return new WP_Error( 'rest_comment_author_data_required', __( 'Creating a comment requires valid author name and email values.' ), array( 'status' => 400 ) ); - } elseif ( ! isset( $prepared_comment['comment_author'] ) ) { - return new WP_Error( 'rest_comment_author_required', __( 'Creating a comment requires a valid author name.' ), array( 'status' => 400 ) ); - } elseif ( ! isset( $prepared_comment['comment_author_email'] ) ) { - return new WP_Error( 'rest_comment_author_email_required', __( 'Creating a comment requires a valid author email.' ), array( 'status' => 400 ) ); - } - } - /** * Filter a comment before it is inserted via the REST API. * diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 3726ab1a17..d15ac07f97 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -775,6 +775,62 @@ public function test_create_item_using_accepted_content_raw_value() { $this->assertEquals( $params['content']['raw'], $new_comment->comment_content ); } + public function test_create_comment_missing_required_author_name_and_email_per_option_value() { + update_option( 'require_name_email', 1 ); + + $params = array( + 'post' => $this->post_id, + 'content' => 'Now, I don\'t want you to worry class. These tests will have no affect on your grades. They merely determine your future social status and financial success. If any.', + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_comment_author_data_required', $response, 400 ); + + update_option( 'require_name_email', 0 ); + } + + public function test_create_comment_missing_required_author_name_per_option_value() { + update_option( 'require_name_email', 1 ); + + $params = array( + 'post' => $this->post_id, + 'author_email' => 'ekrabappel@springfield-elementary.edu', + 'content' => 'Now, I don\'t want you to worry class. These tests will have no affect on your grades. They merely determine your future social status and financial success. If any.', + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_comment_author_required', $response, 400 ); + + update_option( 'require_name_email', 0 ); + } + + public function test_create_comment_missing_required_author_email_per_option_value() { + update_option( 'require_name_email', 1 ); + + $params = array( + 'post' => $this->post_id, + 'author_name' => 'Edna Krabappel', + 'content' => 'Now, I don\'t want you to worry class. These tests will have no affect on your grades. They merely determine your future social status and financial success. If any.', + ); + + $request = new WP_REST_Request( 'POST', '/wp/v2/comments' ); + $request->add_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $params ) ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_comment_author_email_required', $response, 400 ); + + update_option( 'require_name_email', 0 ); + } + public function test_create_item_invalid_blank_content() { wp_set_current_user( 0 ); From 66e937237bcfc1d60e8e0880d2952d2e7b56a6cd Mon Sep 17 00:00:00 2001 From: James Nylen Date: Tue, 18 Oct 2016 21:31:54 -0500 Subject: [PATCH 262/318] Add rest_get_setting filter to override getting setting values --- .../class-wp-rest-settings-controller.php | 22 ++++++++- tests/test-rest-settings-controller.php | 46 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-settings-controller.php b/lib/endpoints/class-wp-rest-settings-controller.php index da833cea28..5f6ececeba 100644 --- a/lib/endpoints/class-wp-rest-settings-controller.php +++ b/lib/endpoints/class-wp-rest-settings-controller.php @@ -51,8 +51,26 @@ public function get_item( $request ) { $response = array(); foreach ( $options as $name => $args ) { - // Default to a null value as "null" in the response means "not set". - $response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] ); + /** + * Filters the value of a setting recognized by the REST API. + * + * Allow hijacking the setting value and overriding the built-in behavior by returning a + * non-null value. The returned value will be presented as the setting value instead. + * + * @since 4.7.0 + * + * @param mixed $result Value to use for the requested setting. Can be a scalar + * matching the registered schema for the setting, or null to + * follow the default `get_option` behavior. + * @param string $name Setting name (as shown in REST API responses). + * @param array $args Arguments passed to `register_setting()` for this setting. + */ + $response[ $name ] = apply_filters( 'rest_get_setting', null, $name, $args ); + + if ( is_null( $response[ $name ] ) ) { + // Default to a null value as "null" in the response means "not set". + $response[ $name ] = get_option( $args['option_name'], $args['schema']['default'] ); + } // Because get_option() is lossy, we have to // cast values to the type they are registered with. diff --git a/tests/test-rest-settings-controller.php b/tests/test-rest-settings-controller.php index 96ada97df9..cb316e00b6 100644 --- a/tests/test-rest-settings-controller.php +++ b/tests/test-rest-settings-controller.php @@ -104,6 +104,52 @@ public function test_get_item_with_custom_setting() { unregister_setting( 'somegroup', 'mycustomsetting' ); } + public function get_setting_custom_callback( $result, $name, $args ) { + switch ( $name ) { + case 'mycustomsetting1': + return 'filtered1'; + } + return $result; + } + + public function test_get_item_with_filter() { + wp_set_current_user( $this->administrator ); + + add_filter( 'rest_get_setting', array( $this, 'get_setting_custom_callback' ), 10, 3 ); + + register_setting( 'somegroup', 'mycustomsetting1', array( + 'show_in_rest' => array( + 'name' => 'mycustomsettinginrest1', + ), + 'type' => 'string', + ) ); + + register_setting( 'somegroup', 'mycustomsetting2', array( + 'show_in_rest' => array( + 'name' => 'mycustomsettinginrest2', + ), + 'type' => 'string', + ) ); + + update_option( 'mycustomsetting1', 'unfiltered1' ); + update_option( 'mycustomsetting2', 'unfiltered2' ); + + $request = new WP_REST_Request( 'GET', '/wp/v2/settings' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + + $this->assertArrayHasKey( 'mycustomsettinginrest1', $data ); + $this->assertEquals( 'unfiltered1', $data['mycustomsettinginrest1'] ); + + $this->assertArrayHasKey( 'mycustomsettinginrest2', $data ); + $this->assertEquals( 'unfiltered2', $data['mycustomsettinginrest2'] ); + + unregister_setting( 'somegroup', 'mycustomsetting' ); + remove_all_filters( 'get_setting_custom_callback' ); + } + public function test_create_item() { } From da162f263e56200b671b2dd0958e901f87d688da Mon Sep 17 00:00:00 2001 From: James Nylen Date: Tue, 18 Oct 2016 21:42:23 -0500 Subject: [PATCH 263/318] Add rest_update_setting filter to override updating setting values --- .../class-wp-rest-settings-controller.php | 20 ++++++++++ tests/test-rest-settings-controller.php | 40 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/lib/endpoints/class-wp-rest-settings-controller.php b/lib/endpoints/class-wp-rest-settings-controller.php index 5f6ececeba..eeb7761b73 100644 --- a/lib/endpoints/class-wp-rest-settings-controller.php +++ b/lib/endpoints/class-wp-rest-settings-controller.php @@ -114,6 +114,26 @@ public function update_item( $request ) { if ( ! array_key_exists( $name, $params ) ) { continue; } + + /** + * Filters whether to preempt a setting value update. + * + * Allow hijacking the setting update logic and overriding the built-in behavior by + * returning true. + * + * @since 4.7.0 + * + * @param boolean $result Whether to override the default behavior for updating the + * value of a setting. + * @param string $name Setting name (as shown in REST API responses). + * @param mixed $value Updated setting value. + * @param array $args Arguments passed to `register_setting()` for this setting. + */ + $updated = apply_filters( 'rest_update_setting', false, $name, $request[ $name ], $args ); + if ( $updated ) { + continue; + } + // A null value means reset the option, which is essentially deleting it // from the database and then relying on the default value. if ( is_null( $request[ $name ] ) ) { diff --git a/tests/test-rest-settings-controller.php b/tests/test-rest-settings-controller.php index cb316e00b6..e7fe6b0126 100644 --- a/tests/test-rest-settings-controller.php +++ b/tests/test-rest-settings-controller.php @@ -165,6 +165,46 @@ public function test_update_item() { $this->assertEquals( get_option( 'blogname' ), $data['title'] ); } + public function update_setting_custom_callback( $result, $name, $value, $args ) { + if ( $name === 'title' && $value === 'The new title!' ) { + // Do not allow changing the title in this case + return true; + } + + return false; + } + + public function test_update_item_with_filter() { + wp_set_current_user( $this->administrator ); + + $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' ); + $request->set_param( 'title', 'The old title!' ); + $request->set_param( 'description', 'The old description!' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'The old title!', $data['title'] ); + $this->assertEquals( 'The old description!', $data['description'] ); + $this->assertEquals( get_option( 'blogname' ), $data['title'] ); + $this->assertEquals( get_option( 'blogdescription' ), $data['description'] ); + + add_filter( 'rest_update_setting', array( $this, 'update_setting_custom_callback' ), 10, 4 ); + + $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' ); + $request->set_param( 'title', 'The new title!' ); + $request->set_param( 'description', 'The new description!' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'The old title!', $data['title'] ); + $this->assertEquals( 'The new description!', $data['description'] ); + $this->assertEquals( get_option( 'blogname' ), $data['title'] ); + $this->assertEquals( get_option( 'blogdescription' ), $data['description'] ); + + remove_all_filters( 'rest_update_setting' ); + } + public function test_update_item_with_invalid_type() { wp_set_current_user( $this->administrator ); $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' ); From 56e8558e8a99c41f68a6eba482b4ea1a2cc7eb27 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Tue, 18 Oct 2016 21:56:38 -0500 Subject: [PATCH 264/318] Get used to it, I will --- tests/test-rest-settings-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test-rest-settings-controller.php b/tests/test-rest-settings-controller.php index e7fe6b0126..a33238d2be 100644 --- a/tests/test-rest-settings-controller.php +++ b/tests/test-rest-settings-controller.php @@ -166,7 +166,7 @@ public function test_update_item() { } public function update_setting_custom_callback( $result, $name, $value, $args ) { - if ( $name === 'title' && $value === 'The new title!' ) { + if ( 'title' === $name && 'The new title!' === $value ) { // Do not allow changing the title in this case return true; } From 29807d6c65834cf4bf3c594be65f362534905dab Mon Sep 17 00:00:00 2001 From: James Nylen Date: Tue, 18 Oct 2016 23:24:43 -0500 Subject: [PATCH 265/318] Update filter names per review feedback --- lib/endpoints/class-wp-rest-settings-controller.php | 4 ++-- tests/test-rest-settings-controller.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/endpoints/class-wp-rest-settings-controller.php b/lib/endpoints/class-wp-rest-settings-controller.php index eeb7761b73..bdb8757923 100644 --- a/lib/endpoints/class-wp-rest-settings-controller.php +++ b/lib/endpoints/class-wp-rest-settings-controller.php @@ -65,7 +65,7 @@ public function get_item( $request ) { * @param string $name Setting name (as shown in REST API responses). * @param array $args Arguments passed to `register_setting()` for this setting. */ - $response[ $name ] = apply_filters( 'rest_get_setting', null, $name, $args ); + $response[ $name ] = apply_filters( 'rest_pre_get_setting', null, $name, $args ); if ( is_null( $response[ $name ] ) ) { // Default to a null value as "null" in the response means "not set". @@ -129,7 +129,7 @@ public function update_item( $request ) { * @param mixed $value Updated setting value. * @param array $args Arguments passed to `register_setting()` for this setting. */ - $updated = apply_filters( 'rest_update_setting', false, $name, $request[ $name ], $args ); + $updated = apply_filters( 'rest_pre_update_setting', false, $name, $request[ $name ], $args ); if ( $updated ) { continue; } diff --git a/tests/test-rest-settings-controller.php b/tests/test-rest-settings-controller.php index a33238d2be..6d4d0a2da0 100644 --- a/tests/test-rest-settings-controller.php +++ b/tests/test-rest-settings-controller.php @@ -115,7 +115,7 @@ public function get_setting_custom_callback( $result, $name, $args ) { public function test_get_item_with_filter() { wp_set_current_user( $this->administrator ); - add_filter( 'rest_get_setting', array( $this, 'get_setting_custom_callback' ), 10, 3 ); + add_filter( 'rest_pre_get_setting', array( $this, 'get_setting_custom_callback' ), 10, 3 ); register_setting( 'somegroup', 'mycustomsetting1', array( 'show_in_rest' => array( @@ -147,7 +147,7 @@ public function test_get_item_with_filter() { $this->assertEquals( 'unfiltered2', $data['mycustomsettinginrest2'] ); unregister_setting( 'somegroup', 'mycustomsetting' ); - remove_all_filters( 'get_setting_custom_callback' ); + remove_all_filters( 'rest_pre_get_setting' ); } public function test_create_item() { @@ -188,7 +188,7 @@ public function test_update_item_with_filter() { $this->assertEquals( get_option( 'blogname' ), $data['title'] ); $this->assertEquals( get_option( 'blogdescription' ), $data['description'] ); - add_filter( 'rest_update_setting', array( $this, 'update_setting_custom_callback' ), 10, 4 ); + add_filter( 'rest_pre_update_setting', array( $this, 'update_setting_custom_callback' ), 10, 4 ); $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' ); $request->set_param( 'title', 'The new title!' ); @@ -202,7 +202,7 @@ public function test_update_item_with_filter() { $this->assertEquals( get_option( 'blogname' ), $data['title'] ); $this->assertEquals( get_option( 'blogdescription' ), $data['description'] ); - remove_all_filters( 'rest_update_setting' ); + remove_all_filters( 'rest_pre_update_setting' ); } public function test_update_item_with_invalid_type() { From 2cabc86a51ba1278f9f11302a2447919d84ab9a6 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 07:12:41 +0200 Subject: [PATCH 266/318] Declare `WP_REST_Terms_Controller->total_terms` property. --- lib/endpoints/class-wp-rest-terms-controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 7f91b31a56..817f830d58 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -13,6 +13,14 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { */ protected $taxonomy; + /** + * Number of terms that were found. + * + * @access protected + * @var int + */ + protected $total_terms; + /** * Constructor. * From 445b178aabc0801307d2f805b026c08edc2eaedc Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 07:16:07 +0200 Subject: [PATCH 267/318] Declare `WP_REST_Terms_Controller->meta` property. --- lib/endpoints/class-wp-rest-terms-controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 817f830d58..a06d206055 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -13,6 +13,14 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { */ protected $taxonomy; + /** + * Instance of a term meta fields object. + * + * @access protected + * @var WP_REST_Term_Meta_Fields + */ + protected $meta; + /** * Number of terms that were found. * From 9b5a0d35ac89b318fee4911f9aca9d1ea6048ef4 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 07:18:46 +0200 Subject: [PATCH 268/318] Declare `WP_REST_Terms_Controller->sort_column` property. --- lib/endpoints/class-wp-rest-terms-controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index a06d206055..45ca89f4b3 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -21,6 +21,14 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { */ protected $meta; + /** + * Column to have the terms be sorted by. + * + * @access protected + * @var string + */ + protected $sort_column; + /** * Number of terms that were found. * From c34ddccc80650f7242b458dbbd9e1a28c86f0dac Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 07:25:17 +0200 Subject: [PATCH 269/318] Declare `WP_REST_Comments_Controller->meta` property. --- lib/endpoints/class-wp-rest-comments-controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 9a8575a6eb..82b626fff7 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -5,6 +5,14 @@ */ class WP_REST_Comments_Controller extends WP_REST_Controller { + /** + * Instance of a comment meta fields object. + * + * @access protected + * @var WP_REST_Comment_Meta_Fields + */ + protected $meta; + public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'comments'; From 2a73b9b40424a805d1b37101d5669fc9a7901dbd Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 07:27:11 +0200 Subject: [PATCH 270/318] Declare `WP_REST_Posts_Controller->meta` property. --- lib/endpoints/class-wp-rest-posts-controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index bbd8fa1374..82f3cb84bd 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -4,6 +4,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { protected $post_type; + /** + * Instance of a post meta fields object. + * + * @access protected + * @var WP_REST_Post_Meta_Fields + */ + protected $meta; + public function __construct( $post_type ) { $this->post_type = $post_type; $this->namespace = 'wp/v2'; From 7fbdbc95d3769fe14637adf9d75038aba531c82a Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 07:30:13 +0200 Subject: [PATCH 271/318] Declare `WP_REST_Users_Controller->meta` property. --- lib/endpoints/class-wp-rest-users-controller.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 92d71616a4..2d56074954 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -5,6 +5,14 @@ */ class WP_REST_Users_Controller extends WP_REST_Controller { + /** + * Instance of a user meta fields object. + * + * @access protected + * @var WP_REST_User_Meta_Fields + */ + protected $meta; + public function __construct() { $this->namespace = 'wp/v2'; $this->rest_base = 'users'; From afd6cc45cea4843d54714c71f9475043ddb6356d Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 07:57:56 +0200 Subject: [PATCH 272/318] Merge multiple `isset()` checks into a single check with multiple arguments. --- lib/endpoints/class-wp-rest-comments-controller.php | 6 +++--- lib/endpoints/class-wp-rest-posts-controller.php | 8 ++++---- lib/endpoints/class-wp-rest-terms-controller.php | 4 ++-- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 9a8575a6eb..c9a076cf51 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -148,7 +148,7 @@ public function get_items( $request ) { // For each known parameter which is both registered and present in the request, // set the parameter's value on the query $prepared_args. foreach ( $parameter_mappings as $api_param => $wp_param ) { - if ( isset( $registered[ $api_param ] ) && isset( $request[ $api_param ] ) ) { + if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { $prepared_args[ $wp_param ] = $request[ $api_param ]; } } @@ -168,12 +168,12 @@ public function get_items( $request ) { $prepared_args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. - if ( isset( $registered['before'] ) && isset( $request['before'] ) ) { + if ( isset( $registered['before'], $request['before'] ) ) { $prepared_args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. - if ( isset( $registered['after'] ) && isset( $request['after'] ) ) { + if ( isset( $registered['after'], $request['after'] ) ) { $prepared_args['date_query'][0]['after'] = $request['after']; } diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index bbd8fa1374..97322b2a06 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -124,7 +124,7 @@ public function get_items( $request ) { // For each known parameter which is both registered and present in the request, // set the parameter's value on the query $args. foreach ( $parameter_mappings as $api_param => $wp_param ) { - if ( isset( $registered[ $api_param ] ) && isset( $request[ $api_param ] ) ) { + if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { $args[ $wp_param ] = $request[ $api_param ]; } } @@ -133,12 +133,12 @@ public function get_items( $request ) { $args['date_query'] = array(); // Set before into date query. Date query must be specified as an array of an array. - if ( isset( $registered['before'] ) && isset( $request['before'] ) ) { + if ( isset( $registered['before'], $request['before'] ) ) { $args['date_query'][0]['before'] = $request['before']; } // Set after into date query. Date query must be specified as an array of an array. - if ( isset( $registered['after'] ) && isset( $request['after'] ) ) { + if ( isset( $registered['after'], $request['after'] ) ) { $args['date_query'][0]['after'] = $request['after']; } @@ -152,7 +152,7 @@ public function get_items( $request ) { $args['posts_per_page'] = $request['per_page']; } - if ( isset( $registered['sticky'] ) && isset( $request['sticky'] ) ) { + if ( isset( $registered['sticky'], $request['sticky'] ) ) { $sticky_posts = get_option( 'sticky_posts', array() ); if ( $sticky_posts && $request['sticky'] ) { // As post__in will be used to only get sticky posts, diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 7f91b31a56..a360679757 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -126,7 +126,7 @@ public function get_items( $request ) { // For each known parameter which is both registered and present in the request, // set the parameter's value on the query $prepared_args. foreach ( $parameter_mappings as $api_param => $wp_param ) { - if ( isset( $registered[ $api_param ] ) && isset( $request[ $api_param ] ) ) { + if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { $prepared_args[ $wp_param ] = $request[ $api_param ]; } } @@ -139,7 +139,7 @@ public function get_items( $request ) { $taxonomy_obj = get_taxonomy( $this->taxonomy ); - if ( $taxonomy_obj->hierarchical && isset( $registered['parent'] ) && isset( $request['parent'] ) ) { + if ( $taxonomy_obj->hierarchical && isset( $registered['parent'], $request['parent'] ) ) { if ( 0 === $request['parent'] ) { // Only query top-level terms. $prepared_args['parent'] = 0; diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 92d71616a4..503026da9d 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -124,7 +124,7 @@ public function get_items( $request ) { // For each known parameter which is both registered and present in the request, // set the parameter's value on the query $prepared_args. foreach ( $parameter_mappings as $api_param => $wp_param ) { - if ( isset( $registered[ $api_param ] ) && isset( $request[ $api_param ] ) ) { + if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { $prepared_args[ $wp_param ] = $request[ $api_param ]; } } From f1f63b13368df4e61861de1660a802a55e449881 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 08:12:47 +0200 Subject: [PATCH 273/318] Merge multiple `unset()` class into a single one with multiple arguments. --- lib/endpoints/class-wp-rest-comments-controller.php | 3 +-- lib/endpoints/class-wp-rest-posts-controller.php | 6 ++++-- lib/endpoints/class-wp-rest-terms-controller.php | 3 +-- lib/endpoints/class-wp-rest-users-controller.php | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 9a8575a6eb..6b9c13eeaf 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -208,8 +208,7 @@ public function get_items( $request ) { $max_pages = (int) $query->max_num_pages; if ( $total_comments < 1 ) { // Out-of-bounds, run the query again without LIMIT for total count - unset( $prepared_args['number'] ); - unset( $prepared_args['offset'] ); + unset( $prepared_args['number'], $prepared_args['offset'] ); $query = new WP_Comment_Query; $prepared_args['count'] = true; diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index bbd8fa1374..faea6037eb 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -259,8 +259,10 @@ public function get_items( $request ) { $request_params = $request->get_query_params(); if ( ! empty( $request_params['filter'] ) ) { // Normalize the pagination params. - unset( $request_params['filter']['posts_per_page'] ); - unset( $request_params['filter']['paged'] ); + unset( + $request_params['filter']['posts_per_page'], + $request_params['filter']['paged'] + ); } $base = add_query_arg( $request_params, rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 7f91b31a56..948c4b2a25 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -171,8 +171,7 @@ public function get_items( $request ) { $query_result = get_terms( $this->taxonomy, $prepared_args ); $count_args = $prepared_args; - unset( $count_args['number'] ); - unset( $count_args['offset'] ); + unset( $count_args['number'], $count_args['offset'] ); $total_terms = wp_count_terms( $this->taxonomy, $count_args ); // wp_count_terms can return a falsy value when the term has no children diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 92d71616a4..eb27876e51 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -190,8 +190,7 @@ public function get_items( $request ) { $total_users = $query->get_total(); if ( $total_users < 1 ) { // Out-of-bounds, run the query again without LIMIT for total count - unset( $prepared_args['number'] ); - unset( $prepared_args['offset'] ); + unset( $prepared_args['number'], $prepared_args['offset'] ); $count_query = new WP_User_Query( $prepared_args ); $total_users = $count_query->get_total(); } From 2f7d221f145b70f301fe0d3bdea93706841a458b Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 08:17:47 +0200 Subject: [PATCH 274/318] Remove redundant `isset()` before `unset()`. --- lib/endpoints/class-wp-rest-controller.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 221266e999..076cefdb1d 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -228,9 +228,7 @@ public function get_public_item_schema() { $schema = $this->get_item_schema(); foreach ( $schema['properties'] as &$property ) { - if ( isset( $property['arg_options'] ) ) { - unset( $property['arg_options'] ); - } + unset( $property['arg_options'] ); } return $schema; From 98ecc8f5db0a0e8f386db6be6aac70ae059cf13b Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 08:21:04 +0200 Subject: [PATCH 275/318] Undo breaking into multiple lines as requested. --- lib/endpoints/class-wp-rest-posts-controller.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index faea6037eb..e8ac64f433 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -259,10 +259,7 @@ public function get_items( $request ) { $request_params = $request->get_query_params(); if ( ! empty( $request_params['filter'] ) ) { // Normalize the pagination params. - unset( - $request_params['filter']['posts_per_page'], - $request_params['filter']['paged'] - ); + unset( $request_params['filter']['posts_per_page'], $request_params['filter']['paged'] ); } $base = add_query_arg( $request_params, rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) ); From 5fec7c294375abfddd0bc78254e5ee77166e9450 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 08:28:53 +0200 Subject: [PATCH 276/318] Avoid slow `in_array()` for single-item arrays. --- lib/endpoints/class-wp-rest-attachments-controller.php | 2 +- lib/endpoints/class-wp-rest-posts-controller.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index a816a1b139..414c789662 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -114,7 +114,7 @@ public function create_item( $request ) { $id = wp_insert_post( $attachment, true ); if ( is_wp_error( $id ) ) { - if ( in_array( $id->get_error_code(), array( 'db_update_error' ), true ) ) { + if ( 'db_update_error' === $id->get_error_code() ) { $id->add_data( array( 'status' => 500 ) ); } else { $id->add_data( array( 'status' => 400 ) ); diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index bbd8fa1374..839ed2e202 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -413,7 +413,7 @@ public function create_item( $request ) { if ( is_wp_error( $post_id ) ) { - if ( in_array( $post_id->get_error_code(), array( 'db_insert_error' ), true ) ) { + if ( 'db_insert_error' === $post_id->get_error_code() ) { $post_id->add_data( array( 'status' => 500 ) ); } else { $post_id->add_data( array( 'status' => 400 ) ); @@ -526,7 +526,7 @@ public function update_item( $request ) { // convert the post object to an array, otherwise wp_update_post will expect non-escaped input $post_id = wp_update_post( (array) $post, true ); if ( is_wp_error( $post_id ) ) { - if ( in_array( $post_id->get_error_code(), array( 'db_update_error' ), true ) ) { + if ( 'db_update_error' === $post_id->get_error_code() ) { $post_id->add_data( array( 'status' => 500 ) ); } else { $post_id->add_data( array( 'status' => 400 ) ); From 66049952b7f23febf2944ab2278855c1966bdefe Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 08:44:41 +0200 Subject: [PATCH 277/318] Replace `in_array()` with `array_keys()` by a simple `isset()`. --- lib/endpoints/class-wp-rest-attachments-controller.php | 2 +- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index 414c789662..bcf34e9f5b 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -16,7 +16,7 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul $query_args['post_status'] = 'inherit'; } $media_types = $this->get_media_types(); - if ( ! empty( $request['media_type'] ) && in_array( $request['media_type'], array_keys( $media_types ), true ) ) { + if ( ! empty( $request['media_type'] ) && isset( $media_types[ $request['media_type'] ] ) ) { $query_args['post_mime_type'] = $media_types[ $request['media_type'] ]; } if ( ! empty( $request['mime_type'] ) ) { diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 839ed2e202..ccbd4b5376 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1602,7 +1602,7 @@ public function get_item_schema() { foreach ( $post_type_attributes as $attribute ) { if ( isset( $fixed_schemas[ $this->post_type ] ) && ! in_array( $attribute, $fixed_schemas[ $this->post_type ], true ) ) { continue; - } elseif ( ! in_array( $this->post_type, array_keys( $fixed_schemas ), true ) && ! post_type_supports( $this->post_type, $attribute ) ) { + } elseif ( ! isset( $fixed_schemas[ $this->post_type ] ) && ! post_type_supports( $this->post_type, $attribute ) ) { continue; } From 05004fc43348ab028516b8b20d2b67cc9bf97c95 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 09:03:09 +0200 Subject: [PATCH 278/318] Remove unnecessary set of parentheses. --- lib/endpoints/class-wp-rest-terms-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 7f91b31a56..629b551087 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -371,7 +371,7 @@ public function create_item( $request ) { * If we're going to inform the client that the term already exists, * give them the identifier for future use. */ - if ( ( $term_id = $term->get_error_data( 'term_exists' ) ) ) { + if ( $term_id = $term->get_error_data( 'term_exists' ) ) { $existing_term = get_term( $term_id, $this->taxonomy ); $term->add_data( $existing_term->term_id, 'term_exists' ); } From 5d840e862b5da65d6ffae5272bd7fd62965a99d2 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 09:30:55 +0200 Subject: [PATCH 279/318] Simplify slug list parsing. --- core-integration.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core-integration.php b/core-integration.php index 1cd9ebdbd2..eeb6fb53df 100644 --- a/core-integration.php +++ b/core-integration.php @@ -28,11 +28,7 @@ function wp_parse_slug_list( $list ) { $list = preg_split( '/[\s,]+/', $list ); } - foreach ( $list as $key => $value ) { - $list[ $key ] = sanitize_title( $value ); - } - - return array_unique( $list ); + return array_unique( array_map( 'sanitize_title', $list ) ); } } From c62b748d80734e9ac56a2c972d12dc947db002e4 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 09:51:48 +0200 Subject: [PATCH 280/318] Replace PHP4 covnersion functions with typecasts. --- lib/endpoints/class-wp-rest-settings-controller.php | 4 ++-- lib/fields/class-wp-rest-meta-fields.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/endpoints/class-wp-rest-settings-controller.php b/lib/endpoints/class-wp-rest-settings-controller.php index da833cea28..4f2b29e6f8 100644 --- a/lib/endpoints/class-wp-rest-settings-controller.php +++ b/lib/endpoints/class-wp-rest-settings-controller.php @@ -72,9 +72,9 @@ public function get_item( $request ) { protected function prepare_value( $value, $schema ) { switch ( $schema['type'] ) { case 'string': - return strval( $value ); + return (string) $value; case 'number': - return floatval( $value ); + return (float) $value; case 'boolean': return (bool) $value; default: diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index 41aed0b2a6..1a06708106 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -345,10 +345,10 @@ public static function prepare_value( $value, $request, $args ) { switch ( $type ) { case 'string': - $value = strval( $value ); + $value = (string) $value; break; case 'number': - $value = floatval( $value ); + $value = (float) $value; break; case 'boolean': $value = (bool) $value; From 4d32f5f06a9a0ba17060e90a6aa391953396c812 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 10:00:48 +0200 Subject: [PATCH 281/318] Fix case for `WP_Http` class. --- lib/fields/class-wp-rest-meta-fields.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index 41aed0b2a6..3286635c9d 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -137,7 +137,7 @@ protected function delete_meta_value( $object_id, $name ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not delete meta value from database.' ), - array( 'key' => $name, 'status' => WP_HTTP::INTERNAL_SERVER_ERROR ) + array( 'key' => $name, 'status' => WP_Http::INTERNAL_SERVER_ERROR ) ); } @@ -191,7 +191,7 @@ protected function update_multi_meta_value( $object_id, $name, $values ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), - array( 'key' => $name, 'status' => WP_HTTP::INTERNAL_SERVER_ERROR ) + array( 'key' => $name, 'status' => WP_Http::INTERNAL_SERVER_ERROR ) ); } } @@ -200,7 +200,7 @@ protected function update_multi_meta_value( $object_id, $name, $values ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), - array( 'key' => $name, 'status' => WP_HTTP::INTERNAL_SERVER_ERROR ) + array( 'key' => $name, 'status' => WP_Http::INTERNAL_SERVER_ERROR ) ); } } @@ -241,7 +241,7 @@ protected function update_meta_value( $object_id, $name, $value ) { return new WP_Error( 'rest_meta_database_error', __( 'Could not update meta value in database.' ), - array( 'key' => $name, 'status' => WP_HTTP::INTERNAL_SERVER_ERROR ) + array( 'key' => $name, 'status' => WP_Http::INTERNAL_SERVER_ERROR ) ); } From 38e08a4bb46b0c6aba2e5638b260d637ca3f0a7d Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 10:16:54 +0200 Subject: [PATCH 282/318] Fix an issue where a static method is called dynamically. --- lib/endpoints/class-wp-rest-attachments-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index a816a1b139..644e156dda 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -382,7 +382,7 @@ protected function upload_from_data( $data, $headers ) { return new WP_Error( 'rest_upload_no_content_disposition', __( 'No Content-Disposition supplied.' ), array( 'status' => 400 ) ); } - $filename = $this->get_filename_from_disposition( $headers['content_disposition'] ); + $filename = self::get_filename_from_disposition( $headers['content_disposition'] ); if ( empty( $filename ) ) { return new WP_Error( 'rest_upload_invalid_disposition', __( 'Invalid Content-Disposition supplied. Content-Disposition needs to be formatted as `attachment; filename="image.png"` or similar.' ), array( 'status' => 400 ) ); From 0568a6a1d5c06d605abfbe512651730dbff37b30 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 10:19:41 +0200 Subject: [PATCH 283/318] Remove misplaced semicolon. --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index bbd8fa1374..edca8476ef 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1381,7 +1381,7 @@ protected function prepare_links( $post ) { 'href' => rest_url( 'wp/v2/users/' . $post->post_author ), 'embeddable' => true, ); - }; + } if ( in_array( $post->post_type, array( 'post', 'page' ), true ) || post_type_supports( $post->post_type, 'comments' ) ) { $replies_url = rest_url( 'wp/v2/comments' ); From bea370d953a75e19eca1909f334109e75232013b Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 12:53:33 +0200 Subject: [PATCH 284/318] Add missing `@return` tag. --- lib/endpoints/class-wp-rest-controller.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 076cefdb1d..7dc9c174b8 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -358,6 +358,7 @@ protected function update_additional_fields_for_object( $object, $request ) { * The type of object is inferred from the passed schema. * * @param array $schema Schema array. + * @return array Modified Schema array. */ protected function add_additional_fields_schema( $schema ) { if ( empty( $schema['title'] ) ) { From 2ea1a7ca3ef5f969153177ac71de5b2f941fa58e Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 12:54:46 +0200 Subject: [PATCH 285/318] Remove superfluous `$format` argument. --- lib/endpoints/class-wp-rest-posts-controller.php | 1 - tests/class-wp-test-rest-post-type-controller-testcase.php | 1 - 2 files changed, 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 4b65c23596..13a00fb52a 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1354,7 +1354,6 @@ public function prepare_item_for_response( $post, $request ) { * "Protected: %s", as the REST API communicates the protected status of a post * in a machine readable format, we remove the "Protected: " prefix. * - * @param string $format * @return string */ public function protected_title_format() { diff --git a/tests/class-wp-test-rest-post-type-controller-testcase.php b/tests/class-wp-test-rest-post-type-controller-testcase.php index 5f49125ae8..0a13d81586 100644 --- a/tests/class-wp-test-rest-post-type-controller-testcase.php +++ b/tests/class-wp-test-rest-post-type-controller-testcase.php @@ -304,7 +304,6 @@ protected function set_raw_post_data( $args = array() ) { * "Protected: %s", as the REST API communicates the protected status of a post * in a machine readable format, we remove the "Protected: " prefix. * - * @param string $format * @return string */ public function protected_title_format() { From 55ceef3460885c57ebb624703c12ff2f1805a9e7 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 12:56:14 +0200 Subject: [PATCH 286/318] Add missing `$taxonomy` argument. --- lib/endpoints/class-wp-rest-terms-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-terms-controller.php b/lib/endpoints/class-wp-rest-terms-controller.php index 57e6291a43..30091317f0 100755 --- a/lib/endpoints/class-wp-rest-terms-controller.php +++ b/lib/endpoints/class-wp-rest-terms-controller.php @@ -895,7 +895,7 @@ public function get_collection_params() { /** * Checks that the taxonomy is valid. * - * @param string + * @param string $taxonomy Taxonomy to check. * @return WP_Error|boolean */ protected function check_is_taxonomy_allowed( $taxonomy ) { From 487baa8db236e928f06f0691d0949e5de28178c1 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 13:02:42 +0200 Subject: [PATCH 287/318] Fix typo in "overrid_d_en". --- lib/endpoints/class-wp-rest-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 076cefdb1d..3e8d2744cd 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -21,7 +21,7 @@ abstract class WP_REST_Controller { * Register the routes for the objects of the controller. */ public function register_routes() { - _doing_it_wrong( 'WP_REST_Controller::register_routes', __( 'The register_routes() method must be overriden' ), 'WPAPI-2.0' ); + _doing_it_wrong( 'WP_REST_Controller::register_routes', __( 'The register_routes() method must be overridden' ), 'WPAPI-2.0' ); } /** From e07d67735f20c03758b41e8b92ce272a910fec55 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 13:03:42 +0200 Subject: [PATCH 288/318] Remove dash in word "overridden". --- lib/endpoints/class-wp-rest-controller.php | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 3e8d2744cd..a6a6cd76c0 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -31,7 +31,7 @@ public function register_routes() { * @return WP_Error|boolean */ public function get_items_permissions_check( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -41,7 +41,7 @@ public function get_items_permissions_check( $request ) { * @return WP_Error|WP_REST_Response */ public function get_items( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -51,7 +51,7 @@ public function get_items( $request ) { * @return WP_Error|boolean */ public function get_item_permissions_check( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -61,7 +61,7 @@ public function get_item_permissions_check( $request ) { * @return WP_Error|WP_REST_Response */ public function get_item( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -71,7 +71,7 @@ public function get_item( $request ) { * @return WP_Error|boolean */ public function create_item_permissions_check( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -81,7 +81,7 @@ public function create_item_permissions_check( $request ) { * @return WP_Error|WP_REST_Response */ public function create_item( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -91,7 +91,7 @@ public function create_item( $request ) { * @return WP_Error|boolean */ public function update_item_permissions_check( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -101,7 +101,7 @@ public function update_item_permissions_check( $request ) { * @return WP_Error|WP_REST_Response */ public function update_item( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -111,7 +111,7 @@ public function update_item( $request ) { * @return WP_Error|boolean */ public function delete_item_permissions_check( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -121,7 +121,7 @@ public function delete_item_permissions_check( $request ) { * @return WP_Error|WP_REST_Response */ public function delete_item( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -131,7 +131,7 @@ public function delete_item( $request ) { * @return WP_Error|object $prepared_item */ protected function prepare_item_for_database( $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** @@ -142,7 +142,7 @@ protected function prepare_item_for_database( $request ) { * @return WP_REST_Response $response */ public function prepare_item_for_response( $item, $request ) { - return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); + return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be overridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); } /** From 8d866a6c9b2caafd418f7627928c0d01bcdfc64a Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 13:04:29 +0200 Subject: [PATCH 289/318] Correct spelling of pagination in comment. --- lib/endpoints/class-wp-rest-users-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 9aaa897f61..a3953b2cbc 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -189,7 +189,7 @@ public function get_items( $request ) { $response = rest_ensure_response( $users ); - // Store pagation values for headers then unset for count query. + // Store pagination values for headers then unset for count query. $per_page = (int) $prepared_args['number']; $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); From 77bde1ceb0a9df54b6e29d3a824d4938ec583a39 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 13:06:06 +0200 Subject: [PATCH 290/318] Replace "passworded" with "password-protected". --- lib/endpoints/class-wp-rest-posts-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 4b65c23596..35a91840d5 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -323,14 +323,14 @@ public function get_item_permissions_check( $request ) { } /** - * Can the user access passworded content? + * Can the user access password-protected content? * * This method determines whether we need to override the regular password * check in core with a filter. * * @param WP_Post $post Post to check against. * @param WP_REST_Request $request Request data to check. - * @return bool True if the user can access passworded content, false otherwise. + * @return bool True if the user can access password-protected content, false otherwise. */ protected function can_access_password_content( $post, $request ) { if ( empty( $post->post_password ) ) { @@ -338,7 +338,7 @@ protected function can_access_password_content( $post, $request ) { return false; } - // Edit context always gets access to passworded posts. + // Edit context always gets access to password-protected posts. if ( 'edit' === $request['context'] ) { return true; } From 0cfddc0296ec1e1ca418aa9e67f4fe3482667411 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 13:07:16 +0200 Subject: [PATCH 291/318] Fix typo in "the_r_ terms". --- lib/endpoints/class-wp-rest-posts-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index 35a91840d5..e934c95193 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -1041,7 +1041,7 @@ public function handle_template( $template, $post_id ) { * * @param int $post_id The post ID to update the terms form. * @param WP_REST_Request $request The request object with post and terms data. - * @return null|WP_Error WP_Error on an error assigning any of ther terms. + * @return null|WP_Error WP_Error on an error assigning any of the terms. */ protected function handle_terms( $post_id, $request ) { $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); From e26a32ef456d79f60c62316907c3df4cb8bb35ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Wed, 19 Oct 2016 13:14:09 +0200 Subject: [PATCH 292/318] No `@access` in methods --- .../class-wp-rest-posts-controller.php | 64 +------------------ 1 file changed, 1 insertion(+), 63 deletions(-) diff --git a/lib/endpoints/class-wp-rest-posts-controller.php b/lib/endpoints/class-wp-rest-posts-controller.php index e41b12568b..5bd3214612 100755 --- a/lib/endpoints/class-wp-rest-posts-controller.php +++ b/lib/endpoints/class-wp-rest-posts-controller.php @@ -13,8 +13,6 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { /** * Constructor. * - * @access public - * * @param string $post_type Post type. */ public function __construct( $post_type ) { @@ -28,8 +26,6 @@ public function __construct( $post_type ) { /** * Register the routes for the objects of the controller. - * - * @access public */ public function register_routes() { @@ -84,8 +80,6 @@ public function register_routes() { /** * Check if a given request has access to read /posts. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ @@ -103,8 +97,6 @@ public function get_items_permissions_check( $request ) { /** * Get a collection of posts. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ @@ -303,8 +295,6 @@ public function get_items( $request ) { /** * Check if a given request has access to read a post. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ @@ -341,8 +331,6 @@ public function get_item_permissions_check( $request ) { * This method determines whether we need to override the regular password * check in core with a filter. * - * @access protected - * * @param WP_Post $post Post to check against. * @param WP_REST_Request $request Request data to check. * @return bool True if the user can access passworded content, false otherwise. @@ -370,8 +358,6 @@ protected function can_access_password_content( $post, $request ) { /** * Get a single post. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ @@ -396,8 +382,6 @@ public function get_item( $request ) { /** * Check if a given request has access to create a post. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ @@ -422,8 +406,6 @@ public function create_item_permissions_check( $request ) { /** * Create a single post. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ @@ -511,8 +493,6 @@ public function create_item( $request ) { /** * Check if a given request has access to update a post. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|boolean */ @@ -539,8 +519,6 @@ public function update_item_permissions_check( $request ) { /** * Update a single post. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_Error|WP_REST_Response */ @@ -619,8 +597,6 @@ public function update_item( $request ) { /** * Check if a given request has access to delete a post. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return bool|WP_Error */ @@ -638,8 +614,6 @@ public function delete_item_permissions_check( $request ) { /** * Delete a single post. * - * @access public - * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error */ @@ -714,8 +688,6 @@ public function delete_item( $request ) { * Determine the allowed query_vars for a get_items() response and * prepare for WP_Query. * - * @access protected - * * @param array $prepared_args Prepared WP_Query Arguments. * @param WP_REST_Request $request Full details about the request. * @return array $query_args @@ -751,8 +723,6 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul /** * Get all the WP Query vars that are allowed for the API request. * - * @access protected - * * @param WP_REST_Request $request Full details about the request. * @return array */ @@ -848,8 +818,6 @@ protected function prepare_date_response( $date_gmt, $date = null ) { /** * Prepare a single post for create or update. * - * @access protected - * * @param WP_REST_Request $request Request object. * @return WP_Error|stdClass $prepared_post Post object. */ @@ -1001,8 +969,6 @@ protected function prepare_item_for_database( $request ) { /** * Determine validity and normalize provided status param. * - * @access protected - * * @param string $post_status Post status. * @param object $post_type Post type. * @return WP_Error|string $post_status @@ -1037,8 +1003,6 @@ protected function handle_status_param( $post_status, $post_type ) { /** * Determine the featured media based on a request param. * - * @access protected - * * @param int $featured_media Featured Media ID. * @param int $post_id Post ID. * @return bool|WP_Error @@ -1062,8 +1026,6 @@ protected function handle_featured_media( $featured_media, $post_id ) { /** * Set the template for a page. * - * @access public - * * @param string $template Page template filename. * @param integer $post_id Post ID. */ @@ -1078,8 +1040,6 @@ public function handle_template( $template, $post_id ) { /** * Update the post's terms from a REST request. * - * @access protected - * * @param int $post_id The post ID to update the terms form. * @param WP_REST_Request $request The request object with post and terms data. * @return null|WP_Error WP_Error on an error assigning any of ther terms. @@ -1103,8 +1063,6 @@ protected function handle_terms( $post_id, $request ) { /** * Check if a given post type should be viewed or managed. * - * @access protected - * * @param object|string $post_type Post type name or object. * @return boolean Is post type allowed? */ @@ -1125,8 +1083,6 @@ protected function check_is_post_type_allowed( $post_type ) { * * Correctly handles posts with the inherit status. * - * @access public - * * @param object $post Post object. * @return boolean Can we read it? */ @@ -1164,8 +1120,6 @@ public function check_read_permission( $post ) { /** * Check if we can edit a post. * - * @access protected - * * @param object $post Post object. * @return boolean Can we edit it? */ @@ -1182,8 +1136,6 @@ protected function check_update_permission( $post ) { /** * Check if we can create a post. * - * @access protected - * * @param object $post Post object. * @return boolean Can we create it?. */ @@ -1200,8 +1152,6 @@ protected function check_create_permission( $post ) { /** * Check if we can delete a post. * - * @access protected - * * @param object $post Post object. * @return boolean Can we delete it? */ @@ -1218,8 +1168,6 @@ protected function check_delete_permission( $post ) { /** * Prepare a single post output for response. * - * @access public - * * @param WP_Post $post Post object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response $data @@ -1407,8 +1355,6 @@ public function prepare_item_for_response( $post, $request ) { * "Protected: %s", as the REST API communicates the protected status of a post * in a machine readable format, we remove the "Protected: " prefix. * - * @access public - * * @return string */ public function protected_title_format() { @@ -1418,8 +1364,6 @@ public function protected_title_format() { /** * Prepare links for the request. * - * @access protected - * * @param WP_Post $post Post object. * @return array Links for the given post. */ @@ -1517,8 +1461,6 @@ protected function prepare_links( $post ) { /** * Get the Post's schema, conforming to JSON Schema. * - * @access public - * * @return array */ public function get_item_schema() { @@ -1848,8 +1790,6 @@ public function get_item_schema() { /** * Get the query params for collections of attachments. * - * @access public - * * @return array */ public function get_collection_params() { @@ -1993,11 +1933,9 @@ public function get_collection_params() { /** * Validate whether the user can query private statuses. * - * @access public - * * @param mixed $value Post status. * @param WP_REST_Request $request Full details about the request. - * @param string $parameter + * @param string $parameter * @return WP_Error|boolean */ public function validate_user_can_query_private_statuses( $value, $request, $parameter ) { From 1b13e9b00b26e8c62419bb6fedda33b30c690623 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 13:18:26 +0200 Subject: [PATCH 293/318] Method `prepare_item_for_response()` can also return a `WP_Error` object. --- lib/endpoints/class-wp-rest-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-controller.php b/lib/endpoints/class-wp-rest-controller.php index 7dc9c174b8..243e573134 100755 --- a/lib/endpoints/class-wp-rest-controller.php +++ b/lib/endpoints/class-wp-rest-controller.php @@ -139,7 +139,7 @@ protected function prepare_item_for_database( $request ) { * * @param mixed $item WordPress representation of the item. * @param WP_REST_Request $request Request object. - * @return WP_REST_Response $response + * @return WP_Error|WP_REST_Response $response */ public function prepare_item_for_response( $item, $request ) { return new WP_Error( 'invalid-method', sprintf( __( "Method '%s' not implemented. Must be over-ridden in subclass." ), __METHOD__ ), array( 'status' => 405 ) ); From 18d72f04e20a012ff2fd61187bd45edd979ece26 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 13:23:53 +0200 Subject: [PATCH 294/318] Correct return type for `get_value()`. --- lib/fields/class-wp-rest-meta-fields.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fields/class-wp-rest-meta-fields.php b/lib/fields/class-wp-rest-meta-fields.php index 41aed0b2a6..2e681ad49d 100644 --- a/lib/fields/class-wp-rest-meta-fields.php +++ b/lib/fields/class-wp-rest-meta-fields.php @@ -36,7 +36,7 @@ public function register_field() { * * @param int $object_id Object ID to fetch meta for. * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|array + * @return WP_Error|object */ public function get_value( $object_id, $request ) { $fields = $this->get_registered_fields(); From d3094aedc6386dec1a9c4d3564115a3e0b89e7fc Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 13:24:49 +0200 Subject: [PATCH 295/318] Add missing `WP_Error` typehints. --- lib/endpoints/class-wp-rest-users-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/endpoints/class-wp-rest-users-controller.php b/lib/endpoints/class-wp-rest-users-controller.php index 9aaa897f61..8a870d6861 100755 --- a/lib/endpoints/class-wp-rest-users-controller.php +++ b/lib/endpoints/class-wp-rest-users-controller.php @@ -299,7 +299,7 @@ public function get_current_item( $request ) { * Check if a given request has access create users * * @param WP_REST_Request $request Full details about the request. - * @return boolean + * @return WP_Error|boolean */ public function create_item_permissions_check( $request ) { @@ -395,7 +395,7 @@ public function create_item( $request ) { * Check if a given request has access update a user * * @param WP_REST_Request $request Full details about the request. - * @return boolean + * @return WP_Error|boolean */ public function update_item_permissions_check( $request ) { @@ -486,7 +486,7 @@ public function update_item( $request ) { * Check if a given request has access delete a user * * @param WP_REST_Request $request Full details about the request. - * @return boolean + * @return WP_Error|boolean */ public function delete_item_permissions_check( $request ) { From e160c80add4addb117c11ce2d5fe3ca42963be32 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 19 Oct 2016 08:31:26 -0400 Subject: [PATCH 296/318] Update the JavaScript client --- wp-api.js | 62 +++++++++++++++++++++++--------------------------- wp-api.min.js | 2 +- wp-api.min.map | 2 +- 3 files changed, 31 insertions(+), 35 deletions(-) mode change 100755 => 100644 wp-api.js diff --git a/wp-api.js b/wp-api.js old mode 100755 new mode 100644 index d1fadcf5b0..0bb5d2e205 --- a/wp-api.js +++ b/wp-api.js @@ -241,51 +241,47 @@ * @type {{toJSON: toJSON, parse: parse}}. */ TimeStampedMixin = { + /** - * Serialize the entity pre-sync. + * Prepare a JavaScript Date for transmitting to the server. + * + * This helper function accepts a field and Date object. It converts the passed Date + * to an ISO string and sets that on the model field. * - * @returns {*}. + * @param {Date} date A JavaScript date object. WordPress expects dates in UTC. + * @param {string} field The date field to set. One of 'date', 'date_gmt', 'date_modified' + * or 'date_modified_gmt'. Optional, defaults to 'date'. */ - toJSON: function() { - var attributes = _.clone( this.attributes ); + setDate: function( date, field ) { + var theField = field || 'date'; - // Serialize Date objects back into 8601 strings. - _.each( parseableDates, function( key ) { - if ( key in attributes ) { - - // Only convert dates. - if ( _.isDate( attributes[ key ] ) ) { - attributes[ key ] = attributes[ key ].toISOString(); - } - } - } ); + // Don't alter non parsable date fields. + if ( _.indexOf( parseableDates, theField ) < 0 ) { + return false; + } - return attributes; + this.set( theField, date.toISOString() ); }, /** - * Unserialize the fetched response. + * Get a JavaScript Date from the passed field. + * + * WordPress returns 'date' and 'date_modified' in the timezone of the server as well as + * UTC dates as 'date_gmt' and 'date_modified_gmt'. Draft posts do not include UTC dates. * - * @param {*} response. - * @returns {*}. + * @param {string} field The date field to set. One of 'date', 'date_gmt', 'date_modified' + * or 'date_modified_gmt'. Optional, defaults to 'date'. */ - parse: function( response ) { - var timestamp; - - // Parse dates into native Date objects. - _.each( parseableDates, function( key ) { - if ( ! ( key in response ) ) { - return; - } + getDate: function( field ) { + var theField = field || 'date', + theISODate = this.get( theField ); - // Don't convert null values. - if ( ! _.isNull( response[ key ] ) ) { - timestamp = wp.api.utils.parseISO8601( response[ key ] ); - response[ key ] = new Date( timestamp ); - } - }); + // Only get date fields and non null values. + if ( _.indexOf( parseableDates, theField ) < 0 || _.isNull( theISODate ) ) { + return false; + } - return response; + return new Date( wp.api.utils.parseISO8601( theISODate ) ); } }, diff --git a/wp-api.min.js b/wp-api.min.js index b6c13c9299..68e17f52a3 100644 --- a/wp-api.min.js +++ b/wp-api.min.js @@ -1,2 +1,2 @@ -!function(a,b){"use strict";function c(){this.models={},this.collections={},this.views={}}a.wp=a.wp||{},wp.api=wp.api||new c,wp.api.versionString=wp.api.versionString||"wp/v2/",!_.isFunction(_.includes)&&_.isFunction(_.contains)&&(_.includes=_.contains)}(window),function(a,b){"use strict";var c,d;a.wp=a.wp||{},wp.api=wp.api||{},wp.api.utils=wp.api.utils||{},Date.prototype.toISOString||(c=function(a){return d=String(a),1===d.length&&(d="0"+d),d},Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+c(this.getUTCMonth()+1)+"-"+c(this.getUTCDate())+"T"+c(this.getUTCHours())+":"+c(this.getUTCMinutes())+":"+c(this.getUTCSeconds())+"."+String((this.getUTCMilliseconds()/1e3).toFixed(3)).slice(2,5)+"Z"}),wp.api.utils.parseISO8601=function(a){var c,d,e,f,g=0,h=[1,4,5,6,7,10,11];if(d=/^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(a)){for(e=0;f=h[e];++e)d[f]=+d[f]||0;d[2]=(+d[2]||1)-1,d[3]=+d[3]||1,"Z"!==d[8]&&b!==d[9]&&(g=60*d[10]+d[11],"+"===d[9]&&(g=0-g)),c=Date.UTC(d[1],d[2],d[3],d[4],d[5]+g,d[6],d[7])}else c=Date.parse?Date.parse(a):NaN;return c},wp.api.utils.getRootUrl=function(){return a.location.origin?a.location.origin+"/":a.location.protocol+"/"+a.location.host+"/"},wp.api.utils.capitalize=function(a){return _.isUndefined(a)?a:a.charAt(0).toUpperCase()+a.slice(1)},wp.api.utils.extractRoutePart=function(a,b){var c;return b=b||1,a=a.replace(wp.api.versionString,""),c=a.split("/").reverse(),_.isUndefined(c[--b])?"":c[b]},wp.api.utils.extractParentName=function(a){var b,c=a.lastIndexOf("_id>[\\d]+)/");return c<0?"":(b=a.substr(0,c-1),b=b.split("/"),b.pop(),b=b.pop())},wp.api.utils.decorateFromRoute=function(a,b){_.each(a,function(a){_.includes(a.methods,"POST")||_.includes(a.methods,"PUT")?_.isEmpty(a.args)||(_.isEmpty(b.prototype.args)?b.prototype.args=a.args:b.prototype.args=_.union(a.args,b.prototype.defaults)):_.includes(a.methods,"GET")&&(_.isEmpty(a.args)||(_.isEmpty(b.prototype.options)?b.prototype.options=a.args:b.prototype.options=_.union(a.args,b.prototype.options)))})},wp.api.utils.addMixinsAndHelpers=function(a,b,c){var d=!1,e=["date","modified","date_gmt","modified_gmt"],f={toJSON:function(){var a=_.clone(this.attributes);return _.each(e,function(b){b in a&&_.isDate(a[b])&&(a[b]=a[b].toISOString())}),a},parse:function(a){var b;return _.each(e,function(c){c in a&&(_.isNull(a[c])||(b=wp.api.utils.parseISO8601(a[c]),a[c]=new Date(b)))}),a}},g=function(a,b,c,d,e){var f,g,h,i;return i=jQuery.Deferred(),g=a.get("_embedded")||{},_.isNumber(b)&&0!==b?(g[d]&&(h=_.findWhere(g[d],{id:b})),h||(h={id:b}),f=new wp.api.models[c](h),f.get(e)?i.resolve(f):f.fetch({success:function(a){i.resolve(a)}}),i.promise()):(i.reject(),i)},h=function(a,b,c,d){var e,f,g,h="",j="",k=jQuery.Deferred();return e=a.get("id"),f=a.get("_embedded")||{},_.isNumber(e)&&0!==e?(_.isUndefined(c)||_.isUndefined(f[c])?h={parent:e}:j=_.isUndefined(d)?f[c]:f[c][d],g=new wp.api.collections[b](j,h),_.isUndefined(g.models[0])?g.fetch({success:function(a){i(a,e),k.resolve(a)}}):(i(g,e),k.resolve(g)),k.promise()):(k.reject(),k)},i=function(a,b){_.each(a.models,function(a){a.set("parent_post",b)})},j={getMeta:function(){return h(this,"PostMeta","https://api.w.org/meta")}},k={getRevisions:function(){return h(this,"PostRevisions")}},l={getTags:function(){var a=this.get("tags"),b=new wp.api.collections.Tags;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setTags:function(a){var b,c,d=this,e=[];return!_.isString(a)&&void(_.isArray(a)?(b=new wp.api.collections.Tags,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Tag(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Tags(e),d.setTagsWithCollection(a)}})):this.setTagsWithCollection(a))},setTagsWithCollection:function(a){return this.set("tags",a.pluck("id")),this.save()}},m={getCategories:function(){var a=this.get("categories"),b=new wp.api.collections.Categories;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setCategories:function(a){var b,c,d=this,e=[];return!_.isString(a)&&void(_.isArray(a)?(b=new wp.api.collections.Categories,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Category(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Categories(e),d.setCategoriesWithCollection(a)}})):this.setCategoriesWithCollection(a))},setCategoriesWithCollection:function(a){return this.set("categories",a.pluck("id")),this.save()}},n={getAuthorUser:function(){return g(this,this.get("author"),"User","author","name")}},o={getFeaturedMedia:function(){return g(this,this.get("featured_media"),"Media","wp:featuredmedia","source_url")}};return _.isUndefined(a.prototype.args)?a:(_.each(e,function(b){_.isUndefined(a.prototype.args[b])||(d=!0)}),d&&(a=a.extend(f)),_.isUndefined(a.prototype.args.author)||(a=a.extend(n)),_.isUndefined(a.prototype.args.featured_media)||(a=a.extend(o)),_.isUndefined(a.prototype.args.categories)||(a=a.extend(m)),_.isUndefined(c.collections[b+"Meta"])||(a=a.extend(j)),_.isUndefined(a.prototype.args.tags)||(a=a.extend(l)),_.isUndefined(c.collections[b+"Revisions"])||(a=a.extend(k)),a)}}(window),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseModel=Backbone.Model.extend({sync:function(b,c,d){var e;return d=d||{},_.isNull(c.get("date_gmt"))&&c.unset("date_gmt"),_.isEmpty(c.get("slug"))&&c.unset("slug"),_.isUndefined(a.nonce)||_.isNull(a.nonce)||(e=d.beforeSend,d.beforeSend=function(b){if(b.setRequestHeader("X-WP-Nonce",a.nonce),e)return e.apply(this,arguments)}),this.requireForceForDelete&&"delete"===b&&(c.url=c.url()+"?force=true"),Backbone.sync(b,c,d)},save:function(a,b){return!(!_.includes(this.methods,"PUT")&&!_.includes(this.methods,"POST"))&&Backbone.Model.prototype.save.call(this,a,b)},destroy:function(a){return!!_.includes(this.methods,"DELETE")&&Backbone.Model.prototype.destroy.call(this,a)}}),wp.api.models.Schema=wp.api.WPApiBaseModel.extend({defaults:{_links:{},namespace:null,routes:{}},initialize:function(b,c){var d=this;c=c||{},wp.api.WPApiBaseModel.prototype.initialize.call(d,b,c),d.apiRoot=c.apiRoot||a.root,d.versionString=c.versionString||a.versionString},url:function(){return this.apiRoot+this.versionString}})}(),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseCollection=Backbone.Collection.extend({initialize:function(a,b){this.state={data:{},currentPage:null,totalPages:null,totalObjects:null},_.isUndefined(b)?this.parent="":this.parent=b.parent},sync:function(b,c,d){var e,f,g=this;return d=d||{},e=d.beforeSend,"undefined"!=typeof a.nonce&&(d.beforeSend=function(b){if(b.setRequestHeader("X-WP-Nonce",a.nonce),e)return e.apply(g,arguments)}),"read"===b&&(d.data?(g.state.data=_.clone(d.data),delete g.state.data.page):g.state.data=d.data={},"undefined"==typeof d.data.page?(g.state.currentPage=null,g.state.totalPages=null,g.state.totalObjects=null):g.state.currentPage=d.data.page-1,f=d.success,d.success=function(a,b,c){if(_.isUndefined(c)||(g.state.totalPages=parseInt(c.getResponseHeader("x-wp-totalpages"),10),g.state.totalObjects=parseInt(c.getResponseHeader("x-wp-total"),10)),null===g.state.currentPage?g.state.currentPage=1:g.state.currentPage++,f)return f.apply(this,arguments)}),Backbone.sync(b,c,d)},more:function(a){if(a=a||{},a.data=a.data||{},_.extend(a.data,this.state.data),"undefined"==typeof a.data.page){if(!this.hasMore())return!1;null===this.state.currentPage||this.state.currentPage<=1?a.data.page=2:a.data.page=this.state.currentPage+1}return this.fetch(a)},hasMore:function(){return null===this.state.totalPages||null===this.state.totalObjects||null===this.state.currentPage?null:this.state.currentPage[\\d]+)/");return 0>c?"":(b=a.substr(0,c-1),b=b.split("/"),b.pop(),b=b.pop())},wp.api.utils.decorateFromRoute=function(a,b){_.each(a,function(a){_.includes(a.methods,"POST")||_.includes(a.methods,"PUT")?_.isEmpty(a.args)||(_.isEmpty(b.prototype.args)?b.prototype.args=a.args:b.prototype.args=_.union(a.args,b.prototype.defaults)):_.includes(a.methods,"GET")&&(_.isEmpty(a.args)||(_.isEmpty(b.prototype.options)?b.prototype.options=a.args:b.prototype.options=_.union(a.args,b.prototype.options)))})},wp.api.utils.addMixinsAndHelpers=function(a,b,c){var d=!1,e=["date","modified","date_gmt","modified_gmt"],f={setDate:function(a,b){var c=b||"date";return _.indexOf(e,c)<0?!1:void this.set(c,a.toISOString())},getDate:function(a){var b=a||"date",c=this.get(b);return _.indexOf(e,b)<0||_.isNull(c)?!1:new Date(wp.api.utils.parseISO8601(c))}},g=function(a,b,c,d,e){var f,g,h,i;return i=jQuery.Deferred(),g=a.get("_embedded")||{},_.isNumber(b)&&0!==b?(g[d]&&(h=_.findWhere(g[d],{id:b})),h||(h={id:b}),f=new wp.api.models[c](h),f.get(e)?i.resolve(f):f.fetch({success:function(a){i.resolve(a)}}),i.promise()):(i.reject(),i)},h=function(a,b,c,d){var e,f,g,h="",j="",k=jQuery.Deferred();return e=a.get("id"),f=a.get("_embedded")||{},_.isNumber(e)&&0!==e?(_.isUndefined(c)||_.isUndefined(f[c])?h={parent:e}:j=_.isUndefined(d)?f[c]:f[c][d],g=new wp.api.collections[b](j,h),_.isUndefined(g.models[0])?g.fetch({success:function(a){i(a,e),k.resolve(a)}}):(i(g,e),k.resolve(g)),k.promise()):(k.reject(),k)},i=function(a,b){_.each(a.models,function(a){a.set("parent_post",b)})},j={getMeta:function(){return h(this,"PostMeta","https://api.w.org/meta")}},k={getRevisions:function(){return h(this,"PostRevisions")}},l={getTags:function(){var a=this.get("tags"),b=new wp.api.collections.Tags;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setTags:function(a){var b,c,d=this,e=[];return _.isString(a)?!1:void(_.isArray(a)?(b=new wp.api.collections.Tags,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Tag(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Tags(e),d.setTagsWithCollection(a)}})):this.setTagsWithCollection(a))},setTagsWithCollection:function(a){return this.set("tags",a.pluck("id")),this.save()}},m={getCategories:function(){var a=this.get("categories"),b=new wp.api.collections.Categories;return _.isEmpty(a)?jQuery.Deferred().resolve([]):b.fetch({data:{include:a}})},setCategories:function(a){var b,c,d=this,e=[];return _.isString(a)?!1:void(_.isArray(a)?(b=new wp.api.collections.Categories,b.fetch({data:{per_page:100},success:function(b){_.each(a,function(a){c=new wp.api.models.Category(b.findWhere({slug:a})),c.set("parent_post",d.get("id")),e.push(c)}),a=new wp.api.collections.Categories(e),d.setCategoriesWithCollection(a)}})):this.setCategoriesWithCollection(a))},setCategoriesWithCollection:function(a){return this.set("categories",a.pluck("id")),this.save()}},n={getAuthorUser:function(){return g(this,this.get("author"),"User","author","name")}},o={getFeaturedMedia:function(){return g(this,this.get("featured_media"),"Media","wp:featuredmedia","source_url")}};return _.isUndefined(a.prototype.args)?a:(_.each(e,function(b){_.isUndefined(a.prototype.args[b])||(d=!0)}),d&&(a=a.extend(f)),_.isUndefined(a.prototype.args.author)||(a=a.extend(n)),_.isUndefined(a.prototype.args.featured_media)||(a=a.extend(o)),_.isUndefined(a.prototype.args.categories)||(a=a.extend(m)),_.isUndefined(c.collections[b+"Meta"])||(a=a.extend(j)),_.isUndefined(a.prototype.args.tags)||(a=a.extend(l)),_.isUndefined(c.collections[b+"Revisions"])||(a=a.extend(k)),a)}}(window),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseModel=Backbone.Model.extend({sync:function(b,c,d){var e;return d=d||{},_.isNull(c.get("date_gmt"))&&c.unset("date_gmt"),_.isEmpty(c.get("slug"))&&c.unset("slug"),_.isUndefined(a.nonce)||_.isNull(a.nonce)||(e=d.beforeSend,d.beforeSend=function(b){return b.setRequestHeader("X-WP-Nonce",a.nonce),e?e.apply(this,arguments):void 0}),this.requireForceForDelete&&"delete"===b&&(c.url=c.url()+"?force=true"),Backbone.sync(b,c,d)},save:function(a,b){return _.includes(this.methods,"PUT")||_.includes(this.methods,"POST")?Backbone.Model.prototype.save.call(this,a,b):!1},destroy:function(a){return _.includes(this.methods,"DELETE")?Backbone.Model.prototype.destroy.call(this,a):!1}}),wp.api.models.Schema=wp.api.WPApiBaseModel.extend({defaults:{_links:{},namespace:null,routes:{}},initialize:function(b,c){var d=this;c=c||{},wp.api.WPApiBaseModel.prototype.initialize.call(d,b,c),d.apiRoot=c.apiRoot||a.root,d.versionString=c.versionString||a.versionString},url:function(){return this.apiRoot+this.versionString}})}(),function(){"use strict";var a=window.wpApiSettings||{};wp.api.WPApiBaseCollection=Backbone.Collection.extend({initialize:function(a,b){this.state={data:{},currentPage:null,totalPages:null,totalObjects:null},_.isUndefined(b)?this.parent="":this.parent=b.parent},sync:function(b,c,d){var e,f,g=this;return d=d||{},e=d.beforeSend,"undefined"!=typeof a.nonce&&(d.beforeSend=function(b){return b.setRequestHeader("X-WP-Nonce",a.nonce),e?e.apply(g,arguments):void 0}),"read"===b&&(d.data?(g.state.data=_.clone(d.data),delete g.state.data.page):g.state.data=d.data={},"undefined"==typeof d.data.page?(g.state.currentPage=null,g.state.totalPages=null,g.state.totalObjects=null):g.state.currentPage=d.data.page-1,f=d.success,d.success=function(a,b,c){return _.isUndefined(c)||(g.state.totalPages=parseInt(c.getResponseHeader("x-wp-totalpages"),10),g.state.totalObjects=parseInt(c.getResponseHeader("x-wp-total"),10)),null===g.state.currentPage?g.state.currentPage=1:g.state.currentPage++,f?f.apply(this,arguments):void 0}),Backbone.sync(b,c,d)},more:function(a){if(a=a||{},a.data=a.data||{},_.extend(a.data,this.state.data),"undefined"==typeof a.data.page){if(!this.hasMore())return!1;null===this.state.currentPage||this.state.currentPage<=1?a.data.page=2:a.data.page=this.state.currentPage+1}return this.fetch(a)},hasMore:function(){return null===this.state.totalPages||null===this.state.totalObjects||null===this.state.currentPage?null:this.state.currentPage Date: Wed, 19 Oct 2016 09:41:16 -0500 Subject: [PATCH 297/318] Change `elseif` to just plain ole `if` statements --- lib/endpoints/class-wp-rest-comments-controller.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 73b1da8135..9895c26a5e 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -388,9 +388,11 @@ public function create_item( $request ) { if ( get_option( 'require_name_email' ) ) { if ( ! isset( $prepared_comment['comment_author'] ) && ! isset( $prepared_comment['comment_author_email'] ) ) { return new WP_Error( 'rest_comment_author_data_required', __( 'Creating a comment requires valid author name and email values.' ), array( 'status' => 400 ) ); - } elseif ( ! isset( $prepared_comment['comment_author'] ) ) { + } + if ( ! isset( $prepared_comment['comment_author'] ) ) { return new WP_Error( 'rest_comment_author_required', __( 'Creating a comment requires a valid author name.' ), array( 'status' => 400 ) ); - } elseif ( ! isset( $prepared_comment['comment_author_email'] ) ) { + } + if ( ! isset( $prepared_comment['comment_author_email'] ) ) { return new WP_Error( 'rest_comment_author_email_required', __( 'Creating a comment requires a valid author email.' ), array( 'status' => 400 ) ); } } From 98b53d551fbb737616a4cfac4788b67f896d7b66 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Wed, 19 Oct 2016 09:42:21 -0500 Subject: [PATCH 298/318] Update inline comment to better explain why/what is happening below --- lib/endpoints/class-wp-rest-comments-controller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/endpoints/class-wp-rest-comments-controller.php b/lib/endpoints/class-wp-rest-comments-controller.php index 9895c26a5e..07f70e4942 100755 --- a/lib/endpoints/class-wp-rest-comments-controller.php +++ b/lib/endpoints/class-wp-rest-comments-controller.php @@ -384,7 +384,8 @@ public function create_item( $request ) { $prepared_comment['comment_author_url'] = $user->user_url; } - // Check author name and email if required. + // Honor the discussion setting that requires a name and email address + // of the comment author. if ( get_option( 'require_name_email' ) ) { if ( ! isset( $prepared_comment['comment_author'] ) && ! isset( $prepared_comment['comment_author_email'] ) ) { return new WP_Error( 'rest_comment_author_data_required', __( 'Creating a comment requires valid author name and email values.' ), array( 'status' => 400 ) ); From 325407cb172d9cfac79f443f64fc6758a1f7adfd Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 17:37:57 +0200 Subject: [PATCH 299/318] Tweak comments. --- .../class-wp-rest-attachments-controller.php | 93 ++++++++++--------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/lib/endpoints/class-wp-rest-attachments-controller.php b/lib/endpoints/class-wp-rest-attachments-controller.php index 2c5b232465..08eb75611b 100755 --- a/lib/endpoints/class-wp-rest-attachments-controller.php +++ b/lib/endpoints/class-wp-rest-attachments-controller.php @@ -6,9 +6,9 @@ class WP_REST_Attachments_Controller extends WP_REST_Posts_Controller { * Determine the allowed query_vars for a get_items() response and * prepare for WP_Query. * - * @param array $prepared_args - * @param WP_REST_Request $request - * @return array $query_args + * @param array $prepared_args Optional. Array of prepared arguments. + * @param WP_REST_Request $request Optional. Request to prepare items for. + * @return array Array of query arguments. */ protected function prepare_items_query( $prepared_args = array(), $request = null ) { $query_args = parent::prepare_items_query( $prepared_args, $request ); @@ -32,7 +32,7 @@ protected function prepare_items_query( $prepared_args = array(), $request = nul * Check if a given request has access to create an attachment. * * @param WP_REST_Request $request Full details about the request. - * @return WP_Error|boolean + * @return WP_Error|true Boolean true if the attachment may be created, or a WP_Error if not. */ public function create_item_permissions_check( $request ) { $ret = parent::create_item_permissions_check( $request ); @@ -44,7 +44,7 @@ public function create_item_permissions_check( $request ) { return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to upload media on this site.' ), array( 'status' => 400 ) ); } - // Attaching media to a post requires ability to edit said post + // Attaching media to a post requires ability to edit said post. if ( ! empty( $request['post'] ) ) { $parent = $this->get_post( (int) $request['post'] ); $post_parent_type = get_post_type_object( $parent->post_type ); @@ -57,10 +57,10 @@ public function create_item_permissions_check( $request ) { } /** - * Create a single attachment + * Create a single attachment. * - * @param WP_REST_Request $request Full details about the request - * @return WP_Error|WP_REST_Response + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|WP_REST_Response Response object on success, WP_Error object on failure. */ public function create_item( $request ) { @@ -123,7 +123,7 @@ public function create_item( $request ) { } $attachment = $this->get_post( $id ); - /** Include admin functions to get access to wp_generate_attachment_metadata() */ + // Include admin functions to get access to wp_generate_attachment_metadata(). require_once ABSPATH . 'wp-admin/includes/admin.php'; wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $file ) ); @@ -157,10 +157,10 @@ public function create_item( $request ) { } /** - * Update a single post + * Update a single post. * - * @param WP_REST_Request $request Full details about the request - * @return WP_Error|WP_REST_Response + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|WP_REST_Response Response object on success, WP_Error object on failure. */ public function update_item( $request ) { if ( ! empty( $request['post'] ) && in_array( get_post_type( $request['post'] ), array( 'revision', 'attachment' ), true ) ) { @@ -196,10 +196,10 @@ public function update_item( $request ) { } /** - * Prepare a single attachment for create or update + * Prepare a single attachment for create or update. * - * @param WP_REST_Request $request Request object - * @return WP_Error|stdClass $prepared_attachment Post object + * @param WP_REST_Request $request Request object. + * @return WP_Error|stdClass $prepared_attachment Post object. */ protected function prepare_item_for_database( $request ) { $prepared_attachment = parent::prepare_item_for_database( $request ); @@ -220,11 +220,11 @@ protected function prepare_item_for_database( $request ) { } /** - * Prepare a single attachment output for response + * Prepare a single attachment output for response. * - * @param WP_Post $post Post object - * @param WP_REST_Request $request Request object - * @return WP_REST_Response $response + * @param WP_Post $post Post object. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response Response object. */ public function prepare_item_for_response( $post, $request ) { $response = parent::prepare_item_for_response( $post, $request ); @@ -239,7 +239,7 @@ public function prepare_item_for_response( $post, $request ) { $data['post'] = ! empty( $post->post_parent ) ? (int) $post->post_parent : null; $data['source_url'] = wp_get_attachment_url( $post->ID ); - // Ensure empty details is an empty object + // Ensure empty details is an empty object. if ( empty( $data['media_details'] ) ) { $data['media_details'] = new stdClass; } elseif ( ! empty( $data['media_details']['sizes'] ) ) { @@ -251,7 +251,7 @@ public function prepare_item_for_response( $post, $request ) { unset( $size_data['mime-type'] ); } - // Use the same method image_downsize() does + // Use the same method image_downsize() does. $image_src = wp_get_attachment_image_src( $post->ID, $size ); if ( ! $image_src ) { continue; @@ -278,7 +278,7 @@ public function prepare_item_for_response( $post, $request ) { $data = $this->filter_response_by_context( $data, $context ); - // Wrap the data in a response object + // Wrap the data in a response object. $response = rest_ensure_response( $data ); $response->add_links( $this->prepare_links( $post ) ); @@ -296,9 +296,9 @@ public function prepare_item_for_response( $post, $request ) { } /** - * Get the Attachment's schema, conforming to JSON Schema + * Get the Attachment's schema, conforming to JSON Schema. * - * @return array + * @return array Item schema as an array. */ public function get_item_schema() { @@ -363,11 +363,11 @@ public function get_item_schema() { } /** - * Handle an upload via raw POST data + * Handle an upload via raw POST data. * - * @param array $data Supplied file data - * @param array $headers HTTP headers from the request - * @return array|WP_Error Data from {@see wp_handle_sideload()} + * @param array $data Supplied file data. + * @param array $headers HTTP headers from the request. + * @return array|WP_Error Data from {@see wp_handle_sideload()}. */ protected function upload_from_data( $data, $headers ) { if ( empty( $data ) ) { @@ -398,13 +398,13 @@ protected function upload_from_data( $data, $headers ) { } } - // Get the content-type + // Get the content-type. $type = array_shift( $headers['content_type'] ); /** Include admin functions to get access to wp_tempnam() and wp_handle_sideload() */ require_once ABSPATH . 'wp-admin/includes/admin.php'; - // Save the file + // Save the file. $tmpfname = wp_tempnam( $filename ); $fp = fopen( $tmpfname, 'w+' ); @@ -416,7 +416,7 @@ protected function upload_from_data( $data, $headers ) { fwrite( $fp, $data ); fclose( $fp ); - // Now, sideload it in + // Now, sideload it in. $file_data = array( 'error' => null, 'tmp_name' => $tmpfname, @@ -466,7 +466,7 @@ protected function upload_from_data( $data, $headers ) { * @return string|null Filename if available, or null if not found. */ public static function get_filename_from_disposition( $disposition_header ) { - // Get the filename + // Get the filename. $filename = null; foreach ( $disposition_header as $value ) { @@ -506,7 +506,7 @@ public static function get_filename_from_disposition( $disposition_header ) { /** * Get the query params for collections of attachments. * - * @return array + * @return array Query parameters for the attachment collection as an array. */ public function get_collection_params() { $params = parent::get_collection_params(); @@ -531,10 +531,10 @@ public function get_collection_params() { /** * Validate whether the user can query private statuses * - * @param mixed $value - * @param WP_REST_Request $request - * @param string $parameter - * @return WP_Error|boolean + * @param mixed $value Status value. + * @param WP_REST_Request $request Request object. + * @param string $parameter Additional parameter to pass to validation. + * @return WP_Error|boolean Boolean true if the user may query, WP_Error if not. */ public function validate_user_can_query_private_statuses( $value, $request, $parameter ) { if ( 'inherit' === $value ) { @@ -544,18 +544,18 @@ public function validate_user_can_query_private_statuses( $value, $request, $par } /** - * Handle an upload via multipart/form-data ($_FILES) + * Handle an upload via multipart/form-data ($_FILES). * - * @param array $files Data from $_FILES - * @param array $headers HTTP headers from the request - * @return array|WP_Error Data from {@see wp_handle_upload()} + * @param array $files Data from $_FILES. + * @param array $headers HTTP headers from the request. + * @return array|WP_Error Data from {@see wp_handle_upload()}. */ protected function upload_from_file( $files, $headers ) { if ( empty( $files ) ) { return new WP_Error( 'rest_upload_no_data', __( 'No data supplied.' ), array( 'status' => 400 ) ); } - // Verify hash, if given + // Verify hash, if given. if ( ! empty( $headers['content_md5'] ) ) { $content_md5 = array_shift( $headers['content_md5'] ); $expected = trim( $content_md5 ); @@ -565,16 +565,16 @@ protected function upload_from_file( $files, $headers ) { } } - // Pass off to WP to handle the actual upload + // Pass off to WP to handle the actual upload. $overrides = array( 'test_form' => false, ); - // Bypasses is_uploaded_file() when running unit tests + // Bypasses is_uploaded_file() when running unit tests. if ( defined( 'DIR_TESTDATA' ) && DIR_TESTDATA ) { $overrides['action'] = 'wp_handle_mock_upload'; } - /** Include admin functions to get access to wp_handle_upload() */ + // Include admin functions to get access to wp_handle_upload(). require_once ABSPATH . 'wp-admin/includes/admin.php'; $file = wp_handle_upload( $files['file'], $overrides ); @@ -587,7 +587,8 @@ protected function upload_from_file( $files, $headers ) { /** * Get the supported media types. - * Media types are considered the MIME type category + * + * Media types are considered the MIME type category. * * @return array */ From 012b1b3b007118f63953c19e40444fbcc29dd7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wrede?= Date: Wed, 19 Oct 2016 17:56:36 +0200 Subject: [PATCH 300/318] Docs: Improve for WP_REST_Term_Meta_Fields --- lib/fields/class-wp-rest-term-meta-fields.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/fields/class-wp-rest-term-meta-fields.php b/lib/fields/class-wp-rest-term-meta-fields.php index 6c4e96224c..3a36ee3154 100644 --- a/lib/fields/class-wp-rest-term-meta-fields.php +++ b/lib/fields/class-wp-rest-term-meta-fields.php @@ -1,5 +1,8 @@ taxonomy = $taxonomy; From 6ad437e370fd77e3872378eca1ed216e6cef3b4d Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Wed, 19 Oct 2016 18:05:50 +0200 Subject: [PATCH 301/318] `in_array()` should only ever be used in strict mode. --- plugin.php | 2 +- tests/test-rest-attachments-controller.php | 12 ++++++------ tests/test-rest-categories-controller.php | 8 ++++---- tests/test-rest-comments-controller.php | 16 ++++++++-------- tests/test-rest-posts-controller.php | 8 ++++---- tests/test-rest-tags-controller.php | 8 ++++---- tests/test-rest-users-controller.php | 8 ++++---- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/plugin.php b/plugin.php index 23ba3330dd..ec2ed266dc 100755 --- a/plugin.php +++ b/plugin.php @@ -455,7 +455,7 @@ function rest_validate_request_arg( $value, $request, $param ) { $args = $attributes['args'][ $param ]; if ( ! empty( $args['enum'] ) ) { - if ( ! in_array( $value, $args['enum'] ) ) { + if ( ! in_array( $value, $args['enum'], true ) ) { return new WP_Error( 'rest_invalid_param', sprintf( /* translators: 1: parameter, 2: list of valid values */ __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) ); } } diff --git a/tests/test-rest-attachments-controller.php b/tests/test-rest-attachments-controller.php index 271e1debcb..7b464b0bc9 100644 --- a/tests/test-rest-attachments-controller.php +++ b/tests/test-rest-attachments-controller.php @@ -169,9 +169,9 @@ public function test_get_items() { $data = $response->get_data(); $this->assertCount( 2, $data ); $ids = wp_list_pluck( $data, 'id' ); - $this->assertTrue( in_array( $id1, $ids ) ); - $this->assertFalse( in_array( $id2, $ids ) ); - $this->assertTrue( in_array( $id3, $ids ) ); + $this->assertTrue( in_array( $id1, $ids, true ) ); + $this->assertFalse( in_array( $id2, $ids, true ) ); + $this->assertTrue( in_array( $id3, $ids, true ) ); $this->check_get_posts_response( $response ); } @@ -198,9 +198,9 @@ public function test_get_items_logged_in_editor() { $data = $response->get_data(); $this->assertCount( 3, $data ); $ids = wp_list_pluck( $data, 'id' ); - $this->assertTrue( in_array( $id1, $ids ) ); - $this->assertTrue( in_array( $id2, $ids ) ); - $this->assertTrue( in_array( $id3, $ids ) ); + $this->assertTrue( in_array( $id1, $ids, true ) ); + $this->assertTrue( in_array( $id2, $ids, true ) ); + $this->assertTrue( in_array( $id3, $ids, true ) ); } public function test_get_items_media_type() { diff --git a/tests/test-rest-categories-controller.php b/tests/test-rest-categories-controller.php index e88a41404d..e2f93898e3 100644 --- a/tests/test-rest-categories-controller.php +++ b/tests/test-rest-categories-controller.php @@ -198,13 +198,13 @@ public function test_get_items_exclude_query() { $request = new WP_REST_Request( 'GET', '/wp/v2/categories' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); $request->set_param( 'exclude', array( $id2 ) ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); } public function test_get_items_orderby_args() { diff --git a/tests/test-rest-comments-controller.php b/tests/test-rest-comments-controller.php index 6c61913399..5a8caabfbe 100644 --- a/tests/test-rest-comments-controller.php +++ b/tests/test-rest-comments-controller.php @@ -150,7 +150,7 @@ public function test_get_items_without_private_post_permission() { $this->assertEquals( 200, $response->get_status() ); $collection_data = $response->get_data(); - $this->assertFalse( in_array( $private_comment, wp_list_pluck( $collection_data, 'id' ) ) ); + $this->assertFalse( in_array( $private_comment, wp_list_pluck( $collection_data, 'id' ), true ) ); } public function test_get_items_with_private_post_permission() { @@ -168,7 +168,7 @@ public function test_get_items_with_private_post_permission() { $this->assertEquals( 200, $response->get_status() ); $collection_data = $response->get_data(); - $this->assertTrue( in_array( $private_comment, wp_list_pluck( $collection_data, 'id' ) ) ); + $this->assertTrue( in_array( $private_comment, wp_list_pluck( $collection_data, 'id' ), true ) ); } public function test_get_items_with_invalid_post() { @@ -185,7 +185,7 @@ public function test_get_items_with_invalid_post() { $this->assertEquals( 200, $response->get_status() ); $collection_data = $response->get_data(); - $this->assertFalse( in_array( $comment_id, wp_list_pluck( $collection_data, 'id' ) ) ); + $this->assertFalse( in_array( $comment_id, wp_list_pluck( $collection_data, 'id' ), true ) ); wp_delete_comment( $comment_id ); } @@ -204,7 +204,7 @@ public function test_get_items_with_invalid_post_permission() { $this->assertEquals( 200, $response->get_status() ); $collection_data = $response->get_data(); - $this->assertTrue( in_array( $comment_id, wp_list_pluck( $collection_data, 'id' ) ) ); + $this->assertTrue( in_array( $comment_id, wp_list_pluck( $collection_data, 'id' ), true ) ); wp_delete_comment( $comment_id ); } @@ -296,13 +296,13 @@ public function test_get_items_exclude_query() { $request = new WP_REST_Request( 'GET', '/wp/v2/comments' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); $request->set_param( 'exclude', array( $id2 ) ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); } public function test_get_items_offset_query() { diff --git a/tests/test-rest-posts-controller.php b/tests/test-rest-posts-controller.php index 5e8a61804d..099584e1ae 100644 --- a/tests/test-rest-posts-controller.php +++ b/tests/test-rest-posts-controller.php @@ -180,13 +180,13 @@ public function test_get_items_exclude_query() { $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); $request->set_param( 'exclude', array( $id2 ) ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); } public function test_get_items_search_query() { diff --git a/tests/test-rest-tags-controller.php b/tests/test-rest-tags-controller.php index bd07114b87..1a9bc92945 100644 --- a/tests/test-rest-tags-controller.php +++ b/tests/test-rest-tags-controller.php @@ -116,13 +116,13 @@ public function test_get_items_exclude_query() { $request = new WP_REST_Request( 'GET', '/wp/v2/tags' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); $request->set_param( 'exclude', array( $id2 ) ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); } public function test_get_items_offset_query() { diff --git a/tests/test-rest-users-controller.php b/tests/test-rest-users-controller.php index 00657c9338..0a7f371cbf 100644 --- a/tests/test-rest-users-controller.php +++ b/tests/test-rest-users-controller.php @@ -403,13 +403,13 @@ public function test_get_items_exclude_query() { $request = new WP_REST_Request( 'GET', '/wp/v2/users' ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertTrue( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); $request->set_param( 'exclude', array( $id2 ) ); $response = $this->server->dispatch( $request ); $data = $response->get_data(); - $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ) ) ); - $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ) ) ); + $this->assertTrue( in_array( $id1, wp_list_pluck( $data, 'id' ), true ) ); + $this->assertFalse( in_array( $id2, wp_list_pluck( $data, 'id' ), true ) ); } public function test_get_items_search() { From c70bbf3b2a9fe5e6bb27c03a5b3bf82681afb991 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 17 Nov 2016 13:52:55 -0600 Subject: [PATCH 302/318] Rewrite CONTRIBUTING.md --- CONTRIBUTING.md | 67 +++++-------------------------------------------- 1 file changed, 6 insertions(+), 61 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 37b3d16d3e..1214a2486f 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,66 +1,11 @@ # Contributing + Hi, and thanks for considering contributing! Before you do though, here's a few notes on how best to contribute. Don't worry, I'll keep it short! -## Getting Started - -### Submitting a pull request - -Contributions to WP-API always follow a feature branch pull request workflow. - -First, create a new branch for your changes. As far as branch naming goes, a -good rule of thumb is to use the issue number followed by a short slug that -represents the feature (e.g. `2392-contributing-docs`). - -Then, submit a pull request early to get a round of feedback on your proposed -changes. It's better to get quick feedback to ensure you're on the right track. - -Next, make sure you have adequate test coverage around your changes. With a -project of this magnitude, it's better to have too much test coverage than not -enough. - -Last, leave a #reviewmerge comment on your pull request for a final review by -the WP-API team. - -### Running tests - -The WP-API project uses two continuous integration (CI) services, Travis and -Scrutinizer, to automatically run a series of tests against its codebase on -every push. If you're submitting a pull request, it's expected that all tests -will pass — and that you add more tests for your change. - -You can install the necessary libraries in your local environment with: - - npm install -g grunt-cli - npm install - composer install - -Then, run PHPUnit tests with `phpunit`, or WPCS tests with `grunt phpcs`. - -## Best Practices - -### Commit Messages -Commit messages should follow the standard laid out in the git manual; that is, -a one-line summary () - - Short (50 chars or less) summary of changes - - More detailed explanatory text, if necessary. Wrap it to about 72 - characters or so. In some contexts, the first line is treated as the - subject of an email and the rest of the text as the body. The blank - line separating the summary from the body is critical (unless you omit - the body entirely); tools like rebase can get confused if you run the - two together. - - Further paragraphs come after blank lines. - - - Bullet points are okay, too - - - Typically a hyphen or asterisk is used for the bullet, preceded by a - single space, with blank lines in between, but conventions vary here +The WP REST API code now lives in core WordPress, so any contributions to the +API (**including new issues**) should be made via +[WordPress core Trac](https://core.trac.wordpress.org). -## Commit Process -Changes are proposed in the form of pull requests by you, the contributor! After -submitting your proposed changes, a member of the API team will review your -commits and mark them for merge by assigning it to themselves. Your pull request -will then be merged after final review by another member. +See also: +[Contributing to WordPress](https://codex.wordpress.org/Contributing_to_WordPress) From 3900988e619e97aae332240feae547f7644cb5cb Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 17 Nov 2016 13:56:09 -0600 Subject: [PATCH 303/318] Rewrite issue tracking section of readme --- README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7e2d84d17e..153901a0d3 100755 --- a/README.md +++ b/README.md @@ -56,8 +56,13 @@ are potentially recent commits to Core that the REST API relies on. See the ## Issue Tracking -All tickets for the project are being tracked on [GitHub][]. You can also take -a look at the [recent updates][] for the project. +All tickets for the project are being tracked on +[WordPress core Trac](https://core.trac.wordpress.org). + +Some previous issues can be found on the +[issue tracker for this repository](/WP-API/WP-API/issues); +however, now that development of the API has moved to core Trac, new issues +**should not be filed here**. ## Contributing @@ -85,7 +90,5 @@ WordPress on your own server. **Do not test on servers you do not own.**) [GPLv2+](http://www.gnu.org/licenses/gpl-2.0.html) [docs]: http://v2.wp-api.org/ -[GitHub]: https://github.com/WP-API/WP-API/issues [contributing]: CONTRIBUTING.md -[recent updates]: https://make.wordpress.org/core/tag/json-api/ [hackerone]: https://hackerone.com/wp-api From ea14da4dff5528c9e64e0ee63724974a0219af5e Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 17 Nov 2016 13:56:46 -0600 Subject: [PATCH 304/318] Update testing instructions based on WP trunk (WIP) --- README.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 153901a0d3..63ca765fe9 100755 --- a/README.md +++ b/README.md @@ -44,15 +44,90 @@ There's no fixed timeline for integration into core at this time, but getting closer! -## Installation +## Quick Setup -Drop this directory into your plugins folder and activate it. You need to be -using pretty permalinks to use the plugin, as it uses custom rewrite rules to -power the API. +Want to test out the WP REST API? The easiest way is just to install a +recent development version of WordPress (for starters, try +[4.7 beta 4](https://wordpress.org/news/2016/11/wordpress-4-7-beta-4/). -Also, be sure to use the Subversion `trunk` branch of WordPress Core as there -are potentially recent commits to Core that the REST API relies on. See the -[WordPress.org website](https://wordpress.org/download/svn/) for simple instructions. +### Testing + +You can also set up a development environment to work on the API code. + +See the +[instructions for running the WordPress PHPUnit test suite](https://make.wordpress.org/core/handbook/testing/automated-testing/phpunit/) +to get started. + +Here is another way to set up a development environment using a virtual +machine: + +1. Install [Vagrant](http://vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/). +2. Clone [Chassis](https://github.com/Chassis/Chassis): + + ```bash + git clone --recursive git@github.com:Chassis/Chassis.git api-tester + ``` + +3. Clone the [Tester extension](https://github.com/Chassis/Tester) for Chassis: + + ```bash + # From your base directory, api-tester if following the steps from before + git clone --recursive https://github.com/Chassis/Tester.git extensions/tester + ``` + +4. Update the `wpdevel` submodule in Chassis to latest on master from [WordPress Git Mirror](https://make.wordpress.org/core/2014/01/15/git-mirrors-for-wordpress/): + + ```bash + # From your base directory, api-tester if following the steps from before + cd extensions/tester/wpdevel + git checkout master + git pull + cd ../../.. + ``` + +5. Start the virtual machine: + + ```bash + vagrant up + ``` + +6. Set the permalink structure to something other than the default, in order to + enable the http://vagrant.local/wp-json/ endpoint URL (if you skip this + step, it can be accessed at http://vagrant.local/?json_route=/): + + ```bash + vagrant ssh -c "cd /vagrant && wp rewrite structure '/%postname%/'" + ``` + +7. Log in to the virtual machine and run the testing suite: + + ```bash + vagrant ssh + cd /vagrant/extensions/tester/wpdevel/ + phpunit --filter REST + ``` + + **TODO: This is broken: PHP Fatal error: Class 'DOMDocument' not found ...** + + **TODO: How to keep `wpdevel/src/` and `/vagrant/wp/` in sync?** + + You can also execute the tests in the context of the VM without SSHing + into the virtual machine (this is equivalent to the above): + + ```bash + vagrant ssh -c 'cd /vagrant/extensions/tester/wpdevel/ && phpunit --filter REST' + ``` + +You're done! You should now have a WordPress site available at +http://vagrant.local; you can access the API via http://vagrant.local/wp-json/ + +To access the admin interface, visit http://vagrant.local/wp/wp-admin and log +in with the credentials below: + + ``` + Username: admin + Password: password + ``` ## Issue Tracking From 7c5bf66cab497073d64150e1cdbb3e377306b019 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 17 Nov 2016 14:16:23 -0600 Subject: [PATCH 305/318] Update note about integration with core --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63ca765fe9..9871d91ee1 100755 --- a/README.md +++ b/README.md @@ -40,8 +40,12 @@ Check out [our documentation][docs] for information on what's available in the API and how to use it. We've also got documentation on extending the API with extra data for plugin and theme developers! -There's no fixed timeline for integration into core at this time, but getting -closer! +The API code in this plugin is currently integrated into core WordPress in the +trunk (latest) and 4.7 beta versions. + +**Development is no longer taking place in this repository** - see +[WordPress core Trac](https://core.trac.wordpress.org) +instead. ## Quick Setup From b46519bff96f21ba1955237ba58d42b3f5155069 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 17 Nov 2016 14:22:17 -0600 Subject: [PATCH 306/318] Remove instructions about setting up a VM --- README.md | 71 ------------------------------------------------------- 1 file changed, 71 deletions(-) diff --git a/README.md b/README.md index 9871d91ee1..e55848fec1 100755 --- a/README.md +++ b/README.md @@ -62,77 +62,6 @@ See the [instructions for running the WordPress PHPUnit test suite](https://make.wordpress.org/core/handbook/testing/automated-testing/phpunit/) to get started. -Here is another way to set up a development environment using a virtual -machine: - -1. Install [Vagrant](http://vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/). -2. Clone [Chassis](https://github.com/Chassis/Chassis): - - ```bash - git clone --recursive git@github.com:Chassis/Chassis.git api-tester - ``` - -3. Clone the [Tester extension](https://github.com/Chassis/Tester) for Chassis: - - ```bash - # From your base directory, api-tester if following the steps from before - git clone --recursive https://github.com/Chassis/Tester.git extensions/tester - ``` - -4. Update the `wpdevel` submodule in Chassis to latest on master from [WordPress Git Mirror](https://make.wordpress.org/core/2014/01/15/git-mirrors-for-wordpress/): - - ```bash - # From your base directory, api-tester if following the steps from before - cd extensions/tester/wpdevel - git checkout master - git pull - cd ../../.. - ``` - -5. Start the virtual machine: - - ```bash - vagrant up - ``` - -6. Set the permalink structure to something other than the default, in order to - enable the http://vagrant.local/wp-json/ endpoint URL (if you skip this - step, it can be accessed at http://vagrant.local/?json_route=/): - - ```bash - vagrant ssh -c "cd /vagrant && wp rewrite structure '/%postname%/'" - ``` - -7. Log in to the virtual machine and run the testing suite: - - ```bash - vagrant ssh - cd /vagrant/extensions/tester/wpdevel/ - phpunit --filter REST - ``` - - **TODO: This is broken: PHP Fatal error: Class 'DOMDocument' not found ...** - - **TODO: How to keep `wpdevel/src/` and `/vagrant/wp/` in sync?** - - You can also execute the tests in the context of the VM without SSHing - into the virtual machine (this is equivalent to the above): - - ```bash - vagrant ssh -c 'cd /vagrant/extensions/tester/wpdevel/ && phpunit --filter REST' - ``` - -You're done! You should now have a WordPress site available at -http://vagrant.local; you can access the API via http://vagrant.local/wp-json/ - -To access the admin interface, visit http://vagrant.local/wp/wp-admin and log -in with the credentials below: - - ``` - Username: admin - Password: password - ``` - ## Issue Tracking All tickets for the project are being tracked on From d274748be74c66d2b82bf1abedd58928e758e9a3 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 17 Nov 2016 23:36:00 -0600 Subject: [PATCH 307/318] ) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e55848fec1..39cfc1034d 100755 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ instead. Want to test out the WP REST API? The easiest way is just to install a recent development version of WordPress (for starters, try -[4.7 beta 4](https://wordpress.org/news/2016/11/wordpress-4-7-beta-4/). +[4.7 beta 4](https://wordpress.org/news/2016/11/wordpress-4-7-beta-4/)). ### Testing From e83e5b1089da92489f2a642d60f545567c78497a Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Sun, 4 Dec 2016 12:59:49 -0500 Subject: [PATCH 308/318] Don't run the plugin on 4.7. --- plugin.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugin.php b/plugin.php index ec2ed266dc..717ff56cc0 100755 --- a/plugin.php +++ b/plugin.php @@ -9,6 +9,16 @@ * License: GPL2+ */ +/** + * No-op on 4.7. This plugin is no longer required when using WordPress 4.7. Though there's + * no big compatibility issues actually running the plugin along with 4.7, there's a chance + * the filters and actions registered can cause odd edgecases. + */ +global $wp_version; +if ( version_compare( $wp_version, '4.7-alpha', '>=' ) ) { + return; +} + /** * WP_REST_Controller class. */ From 0affe3b8d99f446b00486459ca40d7e9c661cd1a Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Mon, 5 Dec 2016 22:58:43 -0500 Subject: [PATCH 309/318] Change Travis CI to build only on latest stable WP build. To prevent testing errors temporarily change travis to build only on latest stable wp build. --- .travis.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8e28abaca1..00baa53ac9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,6 @@ sudo: false matrix: include: - - php: 5.6 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=nightly - php: 5.6 env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest - php: 5.6 @@ -16,17 +14,17 @@ matrix: - php: 5.6 env: WP_TRAVISCI=travis:codecoverage - php: 5.5 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=nightly + env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest - php: 5.4 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=nightly + env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest - php: 5.3 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=nightly + env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest - php: 5.2 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=nightly + env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest - php: hhvm - env: WP_TRAVISCI=travis:phpunit WP_VERSION=nightly + env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest - php: 7.0 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=nightly + env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest allow_failures: - php: hhvm fast_finish: true From 00d797972e4c298b360ee3adfc865b1784d2a4a5 Mon Sep 17 00:00:00 2001 From: BE-Webdesign Date: Mon, 5 Dec 2016 23:04:23 -0500 Subject: [PATCH 310/318] Changing to 4.6.1 Changing the CI to only run on 4.6.1 --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 00baa53ac9..391d386c1f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,23 +8,23 @@ sudo: false matrix: include: - php: 5.6 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest + env: WP_TRAVISCI=travis:phpunit WP_VERSION=4.6.1 - php: 5.6 env: WP_TRAVISCI=travis:phpvalidate - php: 5.6 env: WP_TRAVISCI=travis:codecoverage - php: 5.5 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest + env: WP_TRAVISCI=travis:phpunit WP_VERSION=4.6.1 - php: 5.4 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest + env: WP_TRAVISCI=travis:phpunit WP_VERSION=4.6.1 - php: 5.3 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest + env: WP_TRAVISCI=travis:phpunit WP_VERSION=4.6.1 - php: 5.2 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest + env: WP_TRAVISCI=travis:phpunit WP_VERSION=4.6.1 - php: hhvm - env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest + env: WP_TRAVISCI=travis:phpunit WP_VERSION=4.6.1 - php: 7.0 - env: WP_TRAVISCI=travis:phpunit WP_VERSION=latest + env: WP_TRAVISCI=travis:phpunit WP_VERSION=4.6.1 allow_failures: - php: hhvm fast_finish: true From 4605c21761db8334156467aaed782bd488fee960 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Wed, 7 Dec 2016 03:31:34 -0500 Subject: [PATCH 311/318] Update readme after 4.7 release --- README.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 39cfc1034d..bdcc591991 100755 --- a/README.md +++ b/README.md @@ -1,17 +1,21 @@ -# WP REST API v2.0 (WP-API) +# WP REST API v2.0 (formerly known as WP-API) Access your WordPress site's data through an easy-to-use HTTP REST API. [![Build Status](https://travis-ci.org/WP-API/WP-API.svg?branch=develop)](https://travis-ci.org/WP-API/WP-API) [![codecov.io](http://codecov.io/github/WP-API/WP-API/coverage.svg?branch=develop)](http://codecov.io/github/WP-API/WP-API?branch=develop) -The **"develop"** branch is version 2 which is "beta" but stable and recommended for production. [Read the documentation](http://v2.wp-api.org/) +**Development is no longer taking place in this repository** - see +[WordPress core Trac](https://core.trac.wordpress.org) +instead. + +The **"develop"** branch is version 2 which represents the last "beta" versions of the +[plugin](https://wordpress.org/plugins/rest-api/). +[Read the documentation](https://developer.wordpress.org/rest-api/) to introduce yourself to endpoints, internal patterns, and implementation details. The **"master"** branch represents the **legacy** version of the REST API. -The latest **stable** version is also available from the [WordPress Plugin Directory](https://wordpress.org/plugins/rest-api/). - ## About WordPress is moving towards becoming a fully-fledged application framework, and @@ -40,19 +44,18 @@ Check out [our documentation][docs] for information on what's available in the API and how to use it. We've also got documentation on extending the API with extra data for plugin and theme developers! -The API code in this plugin is currently integrated into core WordPress in the -trunk (latest) and 4.7 beta versions. +The API code in this plugin is currently integrated into core WordPress starting in +[4.7](https://wordpress.org/news/2016/12/vaughan/). **Development is no longer taking place in this repository** - see [WordPress core Trac](https://core.trac.wordpress.org) instead. - ## Quick Setup Want to test out the WP REST API? The easiest way is just to install a -recent development version of WordPress (for starters, try -[4.7 beta 4](https://wordpress.org/news/2016/11/wordpress-4-7-beta-4/)). +recent version of WordPress +([4.7](https://wordpress.org/news/2016/12/vaughan/) or later). ### Testing @@ -97,6 +100,6 @@ WordPress on your own server. **Do not test on servers you do not own.**) [GPLv2+](http://www.gnu.org/licenses/gpl-2.0.html) -[docs]: http://v2.wp-api.org/ +[docs]: https://developer.wordpress.org/rest-api/ [contributing]: CONTRIBUTING.md [hackerone]: https://hackerone.com/wp-api From 2378fa03259cd5d58afd92c81337c5cd4906ab73 Mon Sep 17 00:00:00 2001 From: "K.Adam White" Date: Tue, 13 Dec 2016 23:35:32 -0500 Subject: [PATCH 312/318] Update README to remove mention of `filter` As noted in a recent issue we still mention `filter` in our README documentation, despite having removed the parameter in the core API. This PR updates the plugin README to remove any mention of this parameter, and adds a reference to the oft-missed ?slug= parameter as well; this is less to make the README a canonical source of documentation and more to avoid counteracting the ongoing migration of documentation to developer.wordpress.org --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bdcc591991..8b7578841b 100755 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ site's data in simple JSON format, including users, posts, taxonomies and more. Retrieving or updating data is as simple as sending a HTTP request. Want to get your site's posts? Simply send a `GET` request to `/wp-json/wp/v2/posts`. -Update user with ID 4? Send a `PUT` request to `/wp-json/wp/v2/users/4`. Get all -posts with the search term "awesome"? `GET /wp-json/wp/v2/posts?filter[s]=awesome`. -It's that easy. +Update user with ID 4? Send a `PUT` request to `/wp-json/wp/v2/users/4`. Get the page +with slug "about-me"? `GET /wp-json/wp/v2/pages?slug=about-me`. Get all posts with +the search term "awesome"? `GET /wp-json/wp/v2/posts?search=awesome`. It's that easy. The WordPress REST API exposes a simple yet easy interface to WP Query, the posts API, post meta API, users API, revisions API and many more. Chances are, if you From 0bea5e831ce231b5ff0c32b22df4ca683f5bd1d8 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Wed, 4 Jan 2017 15:46:08 -0600 Subject: [PATCH 313/318] Remove badges failing build; doesn't matter anymore --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 8b7578841b..92d007e57d 100755 --- a/README.md +++ b/README.md @@ -2,9 +2,6 @@ Access your WordPress site's data through an easy-to-use HTTP REST API. -[![Build Status](https://travis-ci.org/WP-API/WP-API.svg?branch=develop)](https://travis-ci.org/WP-API/WP-API) -[![codecov.io](http://codecov.io/github/WP-API/WP-API/coverage.svg?branch=develop)](http://codecov.io/github/WP-API/WP-API?branch=develop) - **Development is no longer taking place in this repository** - see [WordPress core Trac](https://core.trac.wordpress.org) instead. From 013e4d06dca67e200467c8f1ea87ee27e38fa0d9 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Wed, 4 Jan 2017 15:48:31 -0600 Subject: [PATCH 314/318] Direct people to the forums for support requests --- README.md | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 92d007e57d..e5b24d1973 100755 --- a/README.md +++ b/README.md @@ -2,9 +2,14 @@ Access your WordPress site's data through an easy-to-use HTTP REST API. -**Development is no longer taking place in this repository** - see -[WordPress core Trac](https://core.trac.wordpress.org) -instead. +**Development is no longer taking place in this repository**. + +- For support requests, use the + [WordPress forums](https://wordpress.org/support/). +- For bugs and patches, use + [WordPress core Trac](https://core.trac.wordpress.org). + Be sure to include full details and reproduction steps about the issue you are + experiencing, and ideally a patch with unit tests. The **"develop"** branch is version 2 which represents the last "beta" versions of the [plugin](https://wordpress.org/plugins/rest-api/). @@ -44,9 +49,14 @@ extra data for plugin and theme developers! The API code in this plugin is currently integrated into core WordPress starting in [4.7](https://wordpress.org/news/2016/12/vaughan/). -**Development is no longer taking place in this repository** - see -[WordPress core Trac](https://core.trac.wordpress.org) -instead. +**Development is no longer taking place in this repository**. + +- For support requests, use the + [WordPress forums](https://wordpress.org/support/). +- For bugs and patches, use + [WordPress core Trac](https://core.trac.wordpress.org). + Be sure to include full details and reproduction steps about the issue you are + experiencing, and ideally a patch with unit tests. ## Quick Setup From 6febf11ecb001bebd5aa885fe98acba74409cb48 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Thu, 5 Jan 2017 13:00:52 -0500 Subject: [PATCH 315/318] Add github templates to point would-be submitters to trac/forums --- .github/ISSUE_TEMPLATE.md | 10 ++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..1e8b3f7edc --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,10 @@ +Development and support is no longer taking place in this repository. + +Please post support requests to the WordPress forums at https://wordpress.org/support/forum/how-to-and-troubleshooting/ with the topic tag "rest-api". + +For bugs and patches, please post the issue or patch to WordPress core Trac at https://core.trac.wordpress.org -- Be sure to include full details and reproduction steps about the issue you are experiencing. + +If you are unfamiliar with Trac, you can refer to the "Opening a Ticket" user guide at https://make.wordpress.org/core/handbook/tutorials/trac/opening-a-ticket/ for an introduction! + +Thank you, +The REST API team diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..1e8b3f7edc --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +Development and support is no longer taking place in this repository. + +Please post support requests to the WordPress forums at https://wordpress.org/support/forum/how-to-and-troubleshooting/ with the topic tag "rest-api". + +For bugs and patches, please post the issue or patch to WordPress core Trac at https://core.trac.wordpress.org -- Be sure to include full details and reproduction steps about the issue you are experiencing. + +If you are unfamiliar with Trac, you can refer to the "Opening a Ticket" user guide at https://make.wordpress.org/core/handbook/tutorials/trac/opening-a-ticket/ for an introduction! + +Thank you, +The REST API team From 2fd752174f6d20d075f698763877a3ca93f9c776 Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Thu, 5 Jan 2017 13:09:50 -0500 Subject: [PATCH 316/318] Update issue template lead text to explain the "why" --- .github/ISSUE_TEMPLATE.md | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 1e8b3f7edc..11d8e27350 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -Development and support is no longer taking place in this repository. +The REST API has been merged into WordPress core! Development and support is no longer taking place in this repository. Please post support requests to the WordPress forums at https://wordpress.org/support/forum/how-to-and-troubleshooting/ with the topic tag "rest-api". diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1e8b3f7edc..11d8e27350 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,4 @@ -Development and support is no longer taking place in this repository. +The REST API has been merged into WordPress core! Development and support is no longer taking place in this repository. Please post support requests to the WordPress forums at https://wordpress.org/support/forum/how-to-and-troubleshooting/ with the topic tag "rest-api". From 40874ebaa333ad125574a9f590dc359b64aa12fe Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 5 Jan 2017 14:38:16 -0600 Subject: [PATCH 317/318] Minor grammar fix in issue templates --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 11d8e27350..86ed70e654 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -The REST API has been merged into WordPress core! Development and support is no longer taking place in this repository. +The REST API has been merged into WordPress core! Development and support are no longer taking place in this repository. Please post support requests to the WordPress forums at https://wordpress.org/support/forum/how-to-and-troubleshooting/ with the topic tag "rest-api". From 33930b91146636a1eb50809b5be1844914c24aec Mon Sep 17 00:00:00 2001 From: James Nylen Date: Thu, 5 Jan 2017 14:40:07 -0600 Subject: [PATCH 318/318] Minor grammar fix in PR template --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 11d8e27350..86ed70e654 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,4 @@ -The REST API has been merged into WordPress core! Development and support is no longer taking place in this repository. +The REST API has been merged into WordPress core! Development and support are no longer taking place in this repository. Please post support requests to the WordPress forums at https://wordpress.org/support/forum/how-to-and-troubleshooting/ with the topic tag "rest-api".