Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.21% covered (warning)
87.21%
75 / 86
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
WPCOM_REST_API_V2_Endpoint_Newsletter_Email_Sent_Status
90.36% covered (success)
90.36%
75 / 83
50.00% covered (danger)
50.00%
2 / 4
25.56
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 register_routes
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
2
 get_email_sent_status
97.62% covered (success)
97.62%
41 / 42
0.00% covered (danger)
0.00%
0 / 1
15
 permission_check
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2/**
3 * REST API endpoint for post-level newsletter email-sent state.
4 *
5 * On Jetpack sites, registers the route and proxies requests to WordPress.com.
6 * On WordPress.com Simple sites (when required via jetpack-endpoints), handles
7 * the request locally by reading WPCom-only post meta.
8 *
9 * GET /wpcom/v2/newsletter-email-sent-status?post_id=<id>
10 *
11 * @package automattic/jetpack
12 */
13
14use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
15use Automattic\Jetpack\Status\Host;
16
17if ( ! defined( 'ABSPATH' ) ) {
18    exit( 0 );
19}
20
21/**
22 * Class WPCOM_REST_API_V2_Endpoint_Newsletter_Email_Sent_Status
23 */
24class WPCOM_REST_API_V2_Endpoint_Newsletter_Email_Sent_Status extends WP_REST_Controller {
25    use WPCOM_REST_API_Proxy_Request;
26
27    /**
28     * Constructor.
29     */
30    public function __construct() {
31        $this->wpcom_is_wpcom_only_endpoint    = true;
32        $this->wpcom_is_site_specific_endpoint = true;
33        $this->base_api_path                   = 'wpcom';
34        $this->version                         = 'v2';
35        $this->namespace                       = $this->base_api_path . '/' . $this->version;
36        $this->rest_base                       = 'newsletter-email-sent-status';
37
38        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
39    }
40
41    /**
42     * Register routes.
43     */
44    public function register_routes() {
45        register_rest_route(
46            $this->namespace,
47            '/' . $this->rest_base,
48            array(
49                'show_in_index'       => true,
50                'methods'             => 'GET',
51                'callback'            => ( ( new Host() )->is_wpcom_simple() ) ? array( $this, 'get_email_sent_status' ) : array( $this, 'proxy_request_to_wpcom_as_user' ),
52                'permission_callback' => array( $this, 'permission_check' ),
53                'args'                => array(
54                    'post_id' => array(
55                        'required'          => true,
56                        'type'              => 'integer',
57                        'sanitize_callback' => 'absint',
58                        'validate_callback' => function ( $param ) {
59                            return $param > 0;
60                        },
61                    ),
62                ),
63            )
64        );
65    }
66
67    /**
68     * Get email-sent state for a post (WPCom-only meta).
69     *
70     * @param WP_REST_Request $request Request object with post_id.
71     * @return WP_REST_Response|WP_Error
72     */
73    public function get_email_sent_status( $request ) {
74        $post_id = $request->get_param( 'post_id' );
75
76        $post = get_post( $post_id );
77        if ( ! $post instanceof \WP_Post || 'post' !== $post->post_type ) {
78            return new WP_Error(
79                'post_not_found',
80                __( 'Post not found.', 'jetpack' ),
81                array( 'status' => 404 )
82            );
83        }
84
85        $email_notification = get_post_meta( $post_id, 'email_notification', true );
86        $email_sent_at      = null;
87        if ( ! empty( $email_notification ) && is_numeric( $email_notification ) ) {
88            $unix_ts       = (int) $email_notification;
89            $email_sent_at = wp_date( get_option( 'date_format' ), $unix_ts );
90        }
91
92        $stats_meta    = get_post_meta( $post_id, '_wpcom_newsletter_stats_on_email_send', true );
93        $stats_on_send = null;
94        if ( ! empty( $stats_meta ) && is_array( $stats_meta ) && isset( $stats_meta[0] ) ) {
95            $first = $stats_meta[0];
96            $ts    = $first['timestamp'] ?? null;
97            if ( ! empty( $ts ) ) {
98                $unix_ts = strtotime( $ts );
99                $ts      = ( false !== $unix_ts ) ? wp_date( get_option( 'date_format' ), $unix_ts ) : null;
100            } else {
101                $ts = null;
102            }
103
104            $access_level_raw = $first['access_level'] ?? null;
105            $access_level     = $access_level_raw;
106            $paid_tier        = null;
107            if ( is_string( $access_level_raw ) && preg_match( '/^paid_subscribers:\s*(.+)$/', $access_level_raw, $m ) ) {
108                $access_level = 'paid_subscribers';
109                $paid_tier    = trim( $m[1] );
110            }
111
112            $stats_on_send = array(
113                'access_level'              => $access_level,
114                'paid_tier'                 => $paid_tier,
115                'post_categories'           => isset( $first['post_categories'] ) && is_array( $first['post_categories'] ) ? $first['post_categories'] : array(),
116                'has_newsletter_categories' => ! empty( $first['has_newsletter_categories'] ),
117                'has_paywall_block'         => isset( $first['has_paywall_block'] ) ? (bool) $first['has_paywall_block'] : null,
118                'timestamp'                 => $ts,
119            );
120        }
121
122        return rest_ensure_response(
123            array(
124                'email_sent_at' => $email_sent_at,
125                'stats_on_send' => $stats_on_send,
126            )
127        );
128    }
129
130    /**
131     * Permission check for the endpoint.
132     *
133     * @param WP_REST_Request $request Request object.
134     * @return bool|WP_Error
135     */
136    public function permission_check( $request ) {
137        if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'view_stats' ) && ! current_user_can( 'edit_posts' ) ) {
138            return new WP_Error(
139                'rest_forbidden',
140                __( 'Sorry, you are not allowed to access this endpoint.', 'jetpack' ),
141                array( 'status' => rest_authorization_required_code() )
142            );
143        }
144
145        // post_id is validated as > 0 by the route definition; this check is defensive.
146        $post_id = absint( $request->get_param( 'post_id' ) );
147        if ( $post_id > 0 && ! current_user_can( 'manage_options' ) && ! current_user_can( 'edit_post', $post_id ) ) {
148            return new WP_Error(
149                'rest_forbidden',
150                __( 'Sorry, you are not allowed to access this endpoint.', 'jetpack' ),
151                array( 'status' => rest_authorization_required_code() )
152            );
153        }
154
155        return true;
156    }
157}
158
159wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Newsletter_Email_Sent_Status' );