diff --git a/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-send-email-preview.php b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-send-email-preview.php new file mode 100644 index 0000000000000..4fdeccba9312b --- /dev/null +++ b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-send-email-preview.php @@ -0,0 +1,140 @@ +post_type = 'post'; + $this->base_api_path = 'wpcom'; + $this->version = 'v2'; + $this->namespace = $this->base_api_path . '/' . $this->version; + $this->rest_base = '/send-email-preview'; + $this->wpcom_is_wpcom_only_endpoint = true; + $this->wpcom_is_site_specific_endpoint = true; + + add_action( 'rest_api_init', array( $this, 'register_routes' ) ); + } + + /** + * Registers the routes for blogging prompts. + * + * @see register_rest_route() + */ + public function register_routes() { + $options = array( + 'show_in_index' => true, + 'methods' => 'POST', + // if this is not a wpcom site, we need to proxy the request to wpcom + 'callback' => ( ( new Host() )->is_wpcom_simple() ) ? array( $this, 'send_email_preview' ) : array( $this, 'proxy_request_to_wpcom_as_user' ), + 'permission_callback' => array( $this, 'permissions_check' ), + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the post.', 'jetpack' ), + 'type' => 'integer', + ), + ), + ); + + register_rest_route( + $this->namespace, + $this->rest_base, + $options + ); + } + + /** + * Checks if the user is connected and has access to edit the post + * + * @param WP_REST_Request $request Full data about the request. + * + * @return true|WP_Error True if the request has edit access, WP_Error object otherwise. + */ + public function permissions_check( $request ) { + if ( ! ( new Host() )->is_wpcom_simple() ) { + if ( ! ( new Manager() )->is_user_connected() ) { + return new WP_Error( + 'rest_cannot_send_email_preview', + __( 'Please connect your user account to WordPress.com', 'jetpack' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + } + + $post = get_post( $request->get_param( 'id' ) ); + + if ( is_wp_error( $post ) ) { + return $post; + } + + if ( $post && ! current_user_can( 'edit_post', $post->ID ) ) { + return new WP_Error( + 'rest_cannot_send_email_preview', + __( 'Please connect your user account to WordPress.com', 'jetpack' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + return true; + } + + /** + * Sends an email preview of a post to the current user. + * + * @param WP_REST_Request $request Full data about the request. + * + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function send_email_preview( $request ) { + $post_id = $request['id']; + $post = get_post( $post_id ); + + // Return error if the post cannot be retrieved + if ( is_wp_error( $post ) ) { + return $post; + } + + // Check if the user's email is verified + if ( Email_Verification::is_email_unverified() ) { + return new WP_Error( 'unverified', __( 'Your email address must be verified.', 'jetpack' ), array( 'status' => rest_authorization_required_code() ) ); + } + + $current_user = wp_get_current_user(); + $email = $current_user->user_email; + + // Try to create a new subscriber with the user's email + $subscriber = Blog_Subscriber::create( $email ); + if ( ! $subscriber ) { + return new WP_Error( 'unverified', __( 'Could not create subscriber.', 'jetpack' ), array( 'status' => rest_authorization_required_code() ) ); + } + + // Send the post to the subscriber + require_once ABSPATH . 'wp-content/mu-plugins/email-subscriptions/subscription-mailer.php'; + $mailer = new Subscription_Mailer( $subscriber ); + $subscription = $subscriber->get_subscription( get_current_blog_id() ); + $mailer->send_post( $post, $subscription ); + + // Return a response + return new WP_REST_Response( 'Email preview sent successfully.', 200 ); + } + +} + +wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Send_Email_Preview' ); diff --git a/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/trait-wpcom-rest-api-proxy-request-trait.php b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/trait-wpcom-rest-api-proxy-request-trait.php new file mode 100644 index 0000000000000..8d6d79d74fe3b --- /dev/null +++ b/projects/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/trait-wpcom-rest-api-proxy-request-trait.php @@ -0,0 +1,54 @@ +rest_base ) . ( $path ? '/' . rawurldecode( $path ) : '' ); + $api_url = add_query_arg( $request->get_query_params(), $path ); + + $request_options = array( + 'headers' => array( + 'Content-Type' => 'application/json', + 'X-Forwarded-For' => ( new Visitor() )->get_ip( true ), + ), + 'method' => $request->get_method(), + ); + + $response = Client::wpcom_json_api_request_as_user( $api_url, $this->version, $request_options, $request->get_body(), $this->base_api_path ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $response_status = wp_remote_retrieve_response_code( $response ); + $response_body = json_decode( wp_remote_retrieve_body( $response ) ); + + if ( $response_status >= 400 ) { + $code = isset( $response_body->code ) ? $response_body->code : 'unknown_error'; + $message = isset( $response_body->message ) ? $response_body->message : __( 'An unknown error occurred.', 'jetpack' ); + + return new WP_Error( $code, $message, array( 'status' => $response_status ) ); + } + + return $response_body; + } +} diff --git a/projects/plugins/jetpack/changelog/add-preview-email-endpoint b/projects/plugins/jetpack/changelog/add-preview-email-endpoint new file mode 100644 index 0000000000000..6217575372add --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-preview-email-endpoint @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Add send email preview endpoint diff --git a/projects/plugins/jetpack/tests/php/core-api/wpcom-endpoints/test-class-wpcom-rest-api-v2-endpoint-send-email-preview.php b/projects/plugins/jetpack/tests/php/core-api/wpcom-endpoints/test-class-wpcom-rest-api-v2-endpoint-send-email-preview.php new file mode 100644 index 0000000000000..bd8bdbeabff5d --- /dev/null +++ b/projects/plugins/jetpack/tests/php/core-api/wpcom-endpoints/test-class-wpcom-rest-api-v2-endpoint-send-email-preview.php @@ -0,0 +1,129 @@ +user->create( array( 'role' => 'editor' ) ); + static::$user_id_subscriber = self::factory()->user->create( array( 'role' => 'subscriber' ) ); + + static::$path = '/wpcom/v2/send-email-preview'; + + wp_set_current_user( static::$user_id_editor ); + static::$post_id = self::factory()->post->create( + array( + 'post_status' => 'published', + 'post_author' => (string) static::$user_id_editor, + ) + ); + + add_filter( 'pre_option_jetpack_private_options', array( $this, 'mock_jetpack_private_options' ) ); + } + + /** + * Reset the environment to its original state after the test. + */ + public function tear_down() { + remove_filter( 'pre_option_jetpack_private_options', array( $this, 'mock_jetpack_private_options' ) ); + + parent::tear_down(); + } + + /** + * Mock the user's tokens. + * + * @return array + */ + public function mock_jetpack_private_options() { + return array( + 'user_tokens' => array( + static::$user_id_editor => 'pretend_this_is_valid.secret.' . static::$user_id_editor, + static::$user_id_subscriber => 'pretend_this_is_valid.secret.' . static::$user_id_subscriber, + ), + ); + } + + /** + * Test that a non wp.com connected user shouldn't be able to use the endpoint. + * + * @covers ::permissions_check + */ + public function test_email_preview_permissions_check_wrong_user() { + wp_set_current_user( 0 ); + + $request = new WP_REST_Request( Requests::POST, static::$path ); + $request->set_body_params( + array( + 'id' => static::$post_id, + ) + ); + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_cannot_send_email_preview', $response, 401 ); + } + + /** + * Test that a subscriber shouldn't be able to use the endpoint. + * + * @covers ::permissions_check + */ + public function test_email_preview_permissions_check_wrong_role() { + wp_set_current_user( static::$user_id_subscriber ); + + $request = new WP_REST_Request( Requests::POST, static::$path ); + $request->set_body_params( + array( + 'id' => static::$post_id, + ) + ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_forbidden_context', $response, 403 ); + } + +}