diff --git a/admin/class-convertkit-admin-refresh-resources.php b/admin/class-convertkit-admin-refresh-resources.php index 68e7c7ec8..57caddb80 100644 --- a/admin/class-convertkit-admin-refresh-resources.php +++ b/admin/class-convertkit-admin-refresh-resources.php @@ -22,8 +22,30 @@ class ConvertKit_Admin_Refresh_Resources { */ public function __construct() { - add_action( 'wp_ajax_convertkit_admin_refresh_resources', array( $this, 'refresh_resources' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); + add_action( 'rest_api_init', array( $this, 'register_routes' ) ); + + } + + /** + * Register REST API routes. + * + * @since 3.1.0 + */ + public function register_routes() { + + // Register route to return all blocks registered by the Plugin. + register_rest_route( + 'kit/v1', + '/resources/refresh/(?P[a-zA-Z0-9-_]+)', + array( + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'refresh_resources' ), + 'permission_callback' => function () { + return current_user_can( 'edit_posts' ); + }, + ) + ); } @@ -31,14 +53,14 @@ public function __construct() { * Refreshes resources (forms, landing pages or tags) from the API, returning them as a JSON string. * * @since 1.9.8.0 + * + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response|WP_Error Response object. */ - public function refresh_resources() { - - // Check nonce. - check_ajax_referer( 'convertkit_admin_refresh_resources', 'nonce' ); + public function refresh_resources( $request ) { // Get resource type. - $resource = ( isset( $_REQUEST['resource'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['resource'] ) ) : '' ); + $resource = $request->get_param( 'resource' ); // Fetch resources. switch ( $resource ) { @@ -74,7 +96,7 @@ public function refresh_resources() { // Bail if an error occured. if ( is_wp_error( $results_tags ) ) { - wp_send_json_error( $results_tags->get_error_message() ); + return rest_ensure_response( $results_tags ); } // Fetch Products. @@ -83,17 +105,16 @@ public function refresh_resources() { // Bail if an error occured. if ( is_wp_error( $results_products ) ) { - wp_send_json_error( $results_products->get_error_message() ); + return rest_ensure_response( $results_products ); } // Return resources. - wp_send_json_success( + return rest_ensure_response( array( 'tags' => array_values( $results_tags ), 'products' => array_values( $results_products ), ) ); - // no break as wp_send_json_success terminates. default: $results = new WP_Error( @@ -108,11 +129,11 @@ public function refresh_resources() { // Bail if an error occured. if ( is_wp_error( $results ) ) { - wp_send_json_error( $results->get_error_message() ); + return rest_ensure_response( $results ); } // Return resources as a zero based sequential array, so that JS retains the order of resources. - wp_send_json_success( array_values( $results ) ); + return rest_ensure_response( array_values( $results ) ); } @@ -144,10 +165,9 @@ public function enqueue_scripts( $hook ) { 'convertkit-admin-refresh-resources', 'convertkit_admin_refresh_resources', array( - 'action' => 'convertkit_admin_refresh_resources', - 'ajaxurl' => admin_url( 'admin-ajax.php' ), + 'ajaxurl' => rest_url( 'kit/v1/resources/refresh/' ), 'debug' => $settings->debug_enabled(), - 'nonce' => wp_create_nonce( 'convertkit_admin_refresh_resources' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), ) ); diff --git a/includes/class-wp-convertkit.php b/includes/class-wp-convertkit.php index 135757263..f3ba41bfd 100644 --- a/includes/class-wp-convertkit.php +++ b/includes/class-wp-convertkit.php @@ -79,14 +79,14 @@ private function initialize_admin() { return; } - $this->classes['admin_bulk_edit'] = new ConvertKit_Admin_Bulk_Edit(); - $this->classes['admin_cache_plugins'] = new ConvertKit_Admin_Cache_Plugins(); - $this->classes['admin_category'] = new ConvertKit_Admin_Category(); - $this->classes['admin_landing_page'] = new ConvertKit_Admin_Landing_Page(); - $this->classes['admin_notices'] = new ConvertKit_Admin_Notices(); - $this->classes['admin_post'] = new ConvertKit_Admin_Post(); - $this->classes['admin_quick_edit'] = new ConvertKit_Admin_Quick_Edit(); - $this->classes['admin_refresh_resources'] = new ConvertKit_Admin_Refresh_Resources(); + $this->classes['admin_bulk_edit'] = new ConvertKit_Admin_Bulk_Edit(); + $this->classes['admin_cache_plugins'] = new ConvertKit_Admin_Cache_Plugins(); + $this->classes['admin_category'] = new ConvertKit_Admin_Category(); + $this->classes['admin_landing_page'] = new ConvertKit_Admin_Landing_Page(); + $this->classes['admin_notices'] = new ConvertKit_Admin_Notices(); + $this->classes['admin_post'] = new ConvertKit_Admin_Post(); + $this->classes['admin_quick_edit'] = new ConvertKit_Admin_Quick_Edit(); + $this->classes['admin_restrict_content'] = new ConvertKit_Admin_Restrict_Content(); $this->classes['admin_settings'] = new ConvertKit_Admin_Settings(); $this->classes['admin_setup_wizard_landing_page'] = new ConvertKit_Admin_Setup_Wizard_Landing_Page(); @@ -178,6 +178,7 @@ private function initialize_frontend() { */ private function initialize_global() { + $this->classes['admin_refresh_resources'] = new ConvertKit_Admin_Refresh_Resources(); $this->classes['ajax'] = new ConvertKit_AJAX(); $this->classes['blocks_convertkit_broadcasts'] = new ConvertKit_Block_Broadcasts(); $this->classes['blocks_convertkit_content'] = new ConvertKit_Block_Content(); diff --git a/resources/backend/js/refresh-resources.js b/resources/backend/js/refresh-resources.js index e46ad6380..fca71e8d4 100644 --- a/resources/backend/js/refresh-resources.js +++ b/resources/backend/js/refresh-resources.js @@ -49,16 +49,12 @@ function convertKitRefreshResources(button) { button.classList.add('is-refreshing'); // Perform AJAX request to refresh resource. - fetch(convertkit_admin_refresh_resources.ajaxurl, { + fetch(convertkit_admin_refresh_resources.ajaxurl + resource, { method: 'POST', headers: { - 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Type': 'application/json', + 'X-WP-Nonce': convertkit_admin_refresh_resources.nonce, }, - body: new URLSearchParams({ - action: 'convertkit_admin_refresh_resources', - nonce: convertkit_admin_refresh_resources.nonce, - resource, // e.g. forms, landing_pages, tags. - }), }) .then(function (response) { // Convert response JSON string to object. @@ -70,9 +66,9 @@ function convertKitRefreshResources(button) { } // Show an error if the request wasn't successful. - if (!response.success) { + if (typeof response.code !== 'undefined') { // Show error notice. - convertKitRefreshResourcesOutputErrorNotice(response.data); + convertKitRefreshResourcesOutputErrorNotice(response.message); // Enable button and remove is-refreshing class. button.disabled = false; @@ -102,7 +98,7 @@ function convertKitRefreshResources(button) { case 'restrict_content': // Populate select `optgroup`` from response data, which comprises of Tags and Products. // Tags. - response.data.tags.forEach(function (item) { + response.tags.forEach(function (item) { document .querySelector( field + ' optgroup[data-resource=tags]' @@ -118,7 +114,7 @@ function convertKitRefreshResources(button) { }); // Products. - response.data.products.forEach(function (item) { + response.products.forEach(function (item) { document .querySelector( field + ' optgroup[data-resource=products]' @@ -136,7 +132,7 @@ function convertKitRefreshResources(button) { default: // Populate select options from response data. - response.data.forEach(function (item) { + response.forEach(function (item) { // Define label. let label = ''; switch (resource) { diff --git a/tests/Integration/RESTAPITest.php b/tests/Integration/RESTAPITest.php index 52e717f2b..6d30cbc72 100644 --- a/tests/Integration/RESTAPITest.php +++ b/tests/Integration/RESTAPITest.php @@ -82,8 +82,7 @@ public function testGetBlocksWhenUnauthorized() public function testGetBlocks() { // Create and become editor. - $editor_id = static::factory()->user->create( [ 'role' => 'editor' ] ); - wp_set_current_user( $editor_id ); + $this->actAsEditor(); // Send request. $request = new \WP_REST_Request( 'GET', '/kit/v1/blocks' ); @@ -103,4 +102,229 @@ public function testGetBlocks() $this->assertArrayHasKey( 'form-builder-field-custom', $data ); $this->assertArrayHasKey( 'product', $data ); } + + /** + * Test that the /wp-json/kit/v1/resources/refresh REST API route returns a 401 when the user is not authorized. + * + * @since 3.1.0 + */ + public function testRefreshResourcesWhenUnauthorized() + { + // Make request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh/forms' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is unsuccessful. + $this->assertSame( 401, $response->get_status() ); + } + + /** + * Test that the /wp-json/kit/v1/resources/refresh REST API route returns a 404 when the user is authorized and no resource type is provided. + * + * @since 3.1.0 + */ + public function testRefreshResourcesWithNoResourceType() + { + // Create and become editor. + $this->actAsEditor(); + + // Send request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is unsuccessful. + $this->assertSame( 404, $response->get_status() ); + } + + /** + * Test that the /wp-json/kit/v1/resources/refresh REST API route returns a 500 when the user is authorized and an invalid resource type is provided. + * + * @since 3.1.0 + */ + public function testRefreshResourcesWithInvalidResourceType() + { + // Create and become editor. + $this->actAsEditor(); + + // Send request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh/invalid' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is unsuccessful. + $this->assertSame( 500, $response->get_status() ); + } + + /** + * Test that the /wp-json/kit/v1/resources/refresh/forms REST API route refreshes and returns resources when the user is authorized. + * + * @since 3.1.0 + */ + public function testRefreshResourcesForms() + { + // Create and become editor. + $this->actAsEditor(); + + // Send request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh/forms' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is successful. + $this->assertSame( 200, $response->get_status() ); + + // Assert response data has the expected keys. + $data = $response->get_data(); + $this->assertIsArray( $data ); + $this->assertArrayHasKeys( $data[0], [ 'id', 'name', 'created_at', 'type', 'format', 'embed_js', 'embed_url', 'archived', 'uid' ] ); + } + + /** + * Test that the /wp-json/kit/v1/resources/refresh/landing_pages REST API route refreshes and returns resources when the user is authorized. + * + * @since 3.1.0 + */ + public function testRefreshResourcesLandingPages() + { + // Create and become editor. + $this->actAsEditor(); + + // Send request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh/landing_pages' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is successful. + $this->assertSame( 200, $response->get_status() ); + + // Assert response data has the expected keys. + $data = $response->get_data(); + $this->assertIsArray( $data ); + $this->assertArrayHasKeys( $data[0], [ 'id', 'name', 'created_at', 'type', 'format', 'embed_js', 'embed_url', 'archived', 'uid' ] ); + } + + /** + * Test that the /wp-json/kit/v1/resources/refresh/tags REST API route refreshes and returns resources when the user is authorized. + * + * @since 3.1.0 + */ + public function testRefreshResourcesTags() + { + // Create and become editor. + $this->actAsEditor(); + + // Send request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh/tags' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is successful. + $this->assertSame( 200, $response->get_status() ); + + // Assert response data has the expected keys. + $data = $response->get_data(); + $this->assertIsArray( $data ); + $this->assertArrayHasKeys( $data[0], [ 'id', 'name', 'created_at' ] ); + } + + /** + * Test that the /wp-json/kit/v1/resources/refresh/posts REST API route refreshes and returns resources when the user is authorized. + * + * @since 3.1.0 + */ + public function testRefreshResourcesPosts() + { + // Create and become editor. + $this->actAsEditor(); + + // Send request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh/posts' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is successful. + $this->assertSame( 200, $response->get_status() ); + + // Assert response data has the expected keys. + $data = $response->get_data(); + $this->assertIsArray( $data ); + $this->assertArrayHasKeys( $data[0], [ 'id', 'title', 'url', 'published_at', 'is_paid', 'description', 'thumbnail_alt', 'thumbnail_url' ] ); + } + + /** + * Test that the /wp-json/kit/v1/resources/refresh/products REST API route refreshes and returns resources when the user is authorized. + * + * @since 3.1.0 + */ + public function testRefreshResourcesProducts() + { + // Create and become editor. + $this->actAsEditor(); + + // Send request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh/products' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is successful. + $this->assertSame( 200, $response->get_status() ); + + // Assert response data has the expected keys. + $data = $response->get_data(); + $this->assertIsArray( $data ); + $this->assertArrayHasKeys( $data[0], [ 'id', 'name', 'url', 'published' ] ); + } + + /** + * Test that the /wp-json/kit/v1/resources/refresh/restrict_content REST API route refreshes and returns resources when the user is authorized. + * + * @since 3.1.0 + */ + public function testRefreshResourcesRestrictContent() + { + // Create and become editor. + $this->actAsEditor(); + + // Send request. + $request = new \WP_REST_Request( 'POST', '/kit/v1/resources/refresh/restrict_content' ); + $response = rest_get_server()->dispatch( $request ); + + // Assert response is successful. + $this->assertSame( 200, $response->get_status() ); + + // Assert response data has the expected keys. + $data = $response->get_data(); + $this->assertIsArray( $data ); + + // Assert tags response data has the expected keys. + $this->assertArrayHasKey( 'tags', $data ); + $this->assertIsArray( $data['tags'] ); + $this->assertArrayHasKeys( $data['tags'][0], [ 'id', 'name', 'created_at' ] ); + + // Assert products response data has the expected keys. + $this->assertArrayHasKey( 'products', $data ); + $this->assertIsArray( $data['products'] ); + $this->assertArrayHasKeys( $data['products'][0], [ 'id', 'name', 'url', 'published' ] ); + } + + /** + * Act as an editor user. + * + * @since 3.1.0 + */ + private function actAsEditor() + { + $editor_id = static::factory()->user->create( [ 'role' => 'editor' ] ); + wp_set_current_user( $editor_id ); + } + + /** + * Assert that an array has the expected keys. + * + * @since 3.1.0 + * + * @param array $arr The array to assert. + * @param array $keys The keys to assert. + * @return void + */ + private function assertArrayHasKeys( $arr, $keys ) + { + foreach ( $keys as $key ) { + $this->assertArrayHasKey( $key, $arr ); + } + } }