PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
← All changes | _inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-send-email-preview.php +70 -5 13.4.5 → 16.3-a.5 View file →
@@ -5,11 +5,14 @@
5 5 * @package automattic/jetpack
6 6 */
7 7
8 8 use Automattic\Jetpack\Connection\Manager;
9 +use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
9 10 use Automattic\Jetpack\Status\Host;
10 11
11 -require_once __DIR__ . '/trait-wpcom-rest-api-proxy-request-trait.php';
12 +if ( ! defined( 'ABSPATH' ) ) {
13 + exit( 0 );
14 +}
12 15
13 16 /**
14 17 * Class WPCOM_REST_API_V2_Endpoint_Send_Email_Preview
15 18 * Handles the sending of email previews via the WordPress.com REST API
@@ -15,9 +18,9 @@
15 18 * Handles the sending of email previews via the WordPress.com REST API
16 19 */
17 20 class WPCOM_REST_API_V2_Endpoint_Send_Email_Preview extends WP_REST_Controller {
18 21
19 - use WPCOM_REST_API_Proxy_Request_Trait;
22 + use WPCOM_REST_API_Proxy_Request;
20 23
21 24 /**
22 25 * Constructor.
23 26 */
@@ -47,12 +50,16 @@
47 50 'send_email_preview',
48 51 ) : array( $this, 'proxy_request_to_wpcom_as_user' ),
49 52 'permission_callback' => array( $this, 'permissions_check' ),
50 53 'args' => array(
51 - 'id' => array(
54 + 'id' => array(
52 55 'description' => __( 'Unique identifier for the post.', 'jetpack' ),
53 56 'type' => 'integer',
54 57 ),
58 + 'email' => array(
59 + 'description' => __( 'Optional recipient address. Defaults to the current user. A different address is only accepted from users who may add subscribers, and is subject to the same abuse checks.', 'jetpack' ),
60 + 'type' => 'string',
61 + ),
55 62 ),
56 63 );
57 64
58 65 register_rest_route(
@@ -118,11 +125,50 @@
118 125 return new WP_Error( 'unverified', __( 'Your email address must be verified.', 'jetpack' ), array( 'status' => rest_authorization_required_code() ) );
119 126 }
120 127
121 128 $current_user = wp_get_current_user();
122 - $email = $current_user->user_email;
129 + $self_email = $current_user->user_email;
130 + $email = $self_email;
123 131
124 - // Try to create a new subscriber with the user's email
132 + // Resolve the recipient. The address defaults to the caller's own verified
133 + // email; a caller-supplied address is only honored after it clears the same
134 + // gates as adding that person as a subscriber. Self-sends keep their
135 + // historical behavior and skip the guard entirely.
136 + //
137 + // The self-send fast path relies on the caller's own address matching
138 + // $current_user->user_email. That holds because this callback only runs on
139 + // wpcom (is_wpcom_simple(); Atomic/Jetpack requests are proxied to run as the
140 + // wpcom user) — revisit this comparison if it ever runs in another context.
141 + $requested = $request->get_param( 'email' );
142 + if ( is_string( $requested ) && '' !== trim( $requested ) ) {
143 + $requested = sanitize_email( $requested );
144 +
145 + if ( ! is_email( $requested ) ) {
146 + return new WP_Error( 'invalid_email', __( 'Please enter a valid email address.', 'jetpack' ), array( 'status' => 400 ) );
147 + }
148 +
149 + // Normalize both sides: comparing a sanitized address against the raw
150 + // stored email could route a genuine self-send through the guard.
151 + if ( 0 !== strcasecmp( $requested, sanitize_email( $self_email ) ) ) {
152 + $guard = ABSPATH . 'wp-content/mu-plugins/email-subscriptions/email-preview-guard.php';
153 + if ( ! class_exists( 'Email_Preview_Guard' ) && file_exists( $guard ) ) {
154 + require_once $guard;
155 + }
156 +
157 + if ( ! class_exists( 'Email_Preview_Guard' ) ) {
158 + return new WP_Error( 'send_email_preview_guard_unavailable', __( 'Test emails to another address are temporarily unavailable.', 'jetpack' ), array( 'status' => 503 ) );
159 + }
160 +
161 + $guarded = Email_Preview_Guard::check( $requested );
162 + if ( is_wp_error( $guarded ) ) {
163 + return $guarded;
164 + }
165 +
166 + $email = $requested;
167 + }
168 + }
169 +
170 + // Try to create a new subscriber with the resolved email
125 171 $subscriber = Blog_Subscriber::create( $email );
126 172 if ( ! $subscriber ) {
127 173 return new WP_Error( 'unverified', __( 'Could not create subscriber.', 'jetpack' ), array( 'status' => rest_authorization_required_code() ) );
128 174 }
@@ -130,8 +176,27 @@
130 176 // Send the post to the subscriber
131 177 require_once ABSPATH . 'wp-content/mu-plugins/email-subscriptions/subscription-mailer.php';
132 178 $mailer = new Subscription_Mailer( $subscriber );
133 179 $subscription = $subscriber->get_subscription( get_current_blog_id() );
180 +
181 + /**
182 + * Fires immediately before an email preview is dispatched to the current user.
183 + *
184 + * Useful for inspecting the post content with an external classifier (e.g. an
185 + * LLM-based content moderator) or for logging outbound previews. Fires after
186 + * the subscriber has been resolved, so handlers receive a post that is about
187 + * to be sent.
188 + *
189 + * @module subscriptions
190 + *
191 + * @since 15.8
192 + *
193 + * @param WP_Post $post The post being previewed.
194 + * @param Blog_Subscriber $subscriber The subscriber receiving the preview.
195 + * @param Blog_Subscription|false $subscription The subscriber's subscription for the current blog, or false if none exists.
196 + */
197 + do_action( 'jetpack_before_send_email_preview', $post, $subscriber, $subscription );
198 +
134 199 $mailer->send_post( $post, $subscription );
135 200
136 201 // Return a response
137 202 return new WP_REST_Response( 'Email preview sent successfully.', 200 );