Skip to content

Commit

Permalink
[not verified] Add Send Email Preview endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
lezama committed Jun 1, 2023
1 parent b740139 commit c920115
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php
/**
* Handles the sending of email previews via the WordPress.com REST API.
*
* @package automattic/jetpack
*/

use Automattic\Jetpack\Connection\Manager;
use Automattic\Jetpack\Status\Host;

require_once __DIR__ . '/trait-wpcom-rest-api-proxy-request-trait.php';

/**
* Class WPCOM_REST_API_V2_Endpoint_Send_Email_Preview
* Handles the sending of email previews via the WordPress.com REST API
*/
class WPCOM_REST_API_V2_Endpoint_Send_Email_Preview extends WP_REST_Controller {

use WPCOM_REST_API_Proxy_Request_Trait;

/**
* Constructor.
*/
public function __construct() {
$this->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' );
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
/**
* Trait WPCOM_REST_API_Proxy_Request_Trait
*
* Used to proxy requests to wpcom servers.
*
* @package automattic/jetpack
*/

use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Status\Visitor;

trait WPCOM_REST_API_Proxy_Request_Trait {

/**
* Proxy request to wpcom servers for the site and user.
*
* @param WP_Rest_Request $request Request to proxy.
* @param string $path Path to append to the rest base.
*
* @return mixed|WP_Error Response from wpcom servers or an error.
*/
public function proxy_request_to_wpcom_as_user( $request, $path = '' ) {
$blog_id = \Jetpack_Options::get_option( 'id' );
$path = '/sites/' . rawurldecode( $blog_id ) . rawurldecode( $this->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;
}
}
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/add-preview-email-endpoint
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: enhancement

Add send email preview endpoint
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php // phpcs:ignore
/**
* Tests for WPCOM_REST_API_V2_Endpoint_Send_Email_Preview.
* To run this test by itself use the following command:
* jetpack docker phpunit -- --filter=WP_Test_WPCOM_REST_API_V2_Endpoint_Send_Email_Preview
*/

require_once dirname( dirname( __DIR__ ) ) . '/lib/class-wp-test-jetpack-rest-testcase.php';

/**
* Class WP_Test_WPCOM_REST_API_V2_Endpoint_Send_Email_Preview
*
* @coversDefaultClass WPCOM_REST_API_V2_Endpoint_Send_Email_Preview
*/
class WP_Test_WPCOM_REST_API_V2_Endpoint_Send_Email_Preview extends WP_Test_Jetpack_REST_Testcase {

/**
* Mock user ID with editor permissions.
*
* @var int
*/
private static $user_id_editor = 0;

/**
* Mock user ID with subscriber permissions.
*
* @var int
*/
private static $user_id_subscriber = 0;

/**
* Route to endpoint.
*
* @var string
*/
private static $path = '';

/**
* Mock post ID.
*
* @var int
*/
private static $post_id = 0;

/**
* Create 2 mock blog users and a mock blog post.
*/
public function set_up() {
parent::set_up();

static::$user_id_editor = self::factory()->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 );
}

}

0 comments on commit c920115

Please sign in to comment.