Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Podcast_Distribution_Endpoint
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 5
56
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 register
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 register_routes
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 permission_check
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 submit_pocket_casts
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Local Jetpack-side REST proxy for podcast distribution submissions.
4 *
5 * @package automattic/jetpack-podcast
6 */
7
8namespace Automattic\Jetpack\Podcast;
9
10use Automattic\Jetpack\Connection\Client;
11use Jetpack_Options;
12use WP_Error;
13use WP_REST_Controller;
14use WP_REST_Response;
15use WP_REST_Server;
16
17/**
18 * Forwards podcast distribution `wp.apiFetch` calls from the dashboard SPA to
19 * the wpcom-side podcast-distribution endpoints. Same rationale as
20 * Podcast_Stats_Endpoint: wpcom-proxy-request can't authenticate from Atomic
21 * origins, so we proxy server-to-server with the user's token.
22 */
23class Podcast_Distribution_Endpoint extends WP_REST_Controller {
24
25    use Relay_Response;
26
27    /**
28     * Wire up routes.
29     */
30    public static function init() {
31        add_action( 'rest_api_init', array( self::class, 'register' ) );
32    }
33
34    /**
35     * Registers the REST routes on the `rest_api_init` hook.
36     *
37     * Instantiated here, rather than eagerly, so the endpoint class only loads
38     * on requests that reach `rest_api_init`. Static so the callback can be
39     * unregistered.
40     */
41    public static function register() {
42        ( new self() )->register_routes();
43    }
44
45    /**
46     * Register the Pocket Casts submit proxy route.
47     */
48    public function register_routes() {
49        $this->namespace = 'wpcom/v2';
50        $this->rest_base = 'podcast-distribution';
51
52        register_rest_route(
53            $this->namespace,
54            $this->rest_base . '/pocket-casts/submit',
55            array(
56                'methods'             => WP_REST_Server::CREATABLE,
57                'callback'            => array( $this, 'submit_pocket_casts' ),
58                'permission_callback' => array( $this, 'permission_check' ),
59            )
60        );
61    }
62
63    /**
64     * Permission callback. Submitting a feed mutates the site's distribution
65     * state, so require the same edit permission the dashboard SPA itself uses.
66     *
67     * @return true|WP_Error
68     */
69    public function permission_check() {
70        if ( ! current_user_can( 'edit_posts' ) ) {
71            return new WP_Error(
72                'rest_forbidden',
73                __( 'Sorry, you are not allowed to submit podcasts for this site.', 'jetpack-podcast' ),
74                array( 'status' => rest_authorization_required_code() )
75            );
76        }
77        return true;
78    }
79
80    /**
81     * POST /wpcom/v2/podcast-distribution/pocket-casts/submit — forward to wpcom.
82     *
83     * @return WP_REST_Response|WP_Error
84     */
85    public function submit_pocket_casts() {
86        $blog_id = (int) Jetpack_Options::get_option( 'id' );
87        if ( ! $blog_id ) {
88            return new WP_Error(
89                'site-not-connected',
90                __( 'Site is not connected to WordPress.com.', 'jetpack-podcast' ),
91                array( 'status' => 400 )
92            );
93        }
94
95        $response = Client::wpcom_json_api_request_as_user(
96            sprintf( '/sites/%d/podcast-distribution/pocket-casts/submit', $blog_id ),
97            '2',
98            array(
99                // Pocket Casts relay can be slow; keep a generous timeout.
100                'method'  => 'POST',
101                'headers' => array( 'content-type' => 'application/json' ),
102                'timeout' => 30,
103            ),
104            null,
105            'wpcom'
106        );
107
108        return $this->relay_response( $response );
109    }
110}