Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
61.33% covered (warning)
61.33%
46 / 75
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Share_Post_Controller
63.01% covered (warning)
63.01%
46 / 73
50.00% covered (danger)
50.00%
2 / 4
15.06
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 register_routes
79.59% covered (warning)
79.59%
39 / 49
0.00% covered (danger)
0.00%
0 / 1
3.08
 permissions_check
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 share_post
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2/**
3 * Publicize: Share post
4 *
5 * @package automattic/jetpack-publicize
6 */
7
8namespace Automattic\Jetpack\Publicize\REST_API;
9
10use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
11use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
12use WP_Error;
13use WP_REST_Request;
14use WP_REST_Response;
15use WP_REST_Server;
16
17if ( ! defined( 'ABSPATH' ) ) {
18    exit( 0 );
19}
20
21/**
22 * Publicize: Share post class.
23 *
24 * @phan-constructor-used-for-side-effects
25 */
26class Share_Post_Controller extends Base_Controller {
27
28    use WPCOM_REST_API_Proxy_Request;
29
30    /**
31     * The constructor sets the route namespace, rest_base, and registers our API route and endpoint.
32     */
33    public function __construct() {
34        parent::__construct();
35
36        $this->base_api_path = 'wpcom';
37        $this->version       = 'v2';
38
39        $this->namespace = "{$this->base_api_path}/{$this->version}";
40        $this->rest_base = 'publicize/share-post';
41
42        add_action( 'rest_api_init', array( $this, 'register_routes' ) );
43    }
44
45    /**
46     * Register the routes.
47     */
48    public function register_routes() {
49
50        $args = array(
51            'methods'             => WP_REST_Server::CREATABLE,
52            'callback'            => array( $this, 'share_post' ),
53            'permission_callback' => array( $this, 'permissions_check' ),
54            'args'                => array(
55                'message'             => array(
56                    'description'       => __( 'The message to share.', 'jetpack-publicize-pkg' ),
57                    'type'              => 'string',
58                    'required'          => true,
59                    'validate_callback' => function ( $param ) {
60                        return is_string( $param );
61                    },
62                    'sanitize_callback' => 'sanitize_textarea_field',
63                ),
64                'skipped_connections' => array(
65                    'description'       => __( 'Array of external connection IDs to skip sharing.', 'jetpack-publicize-pkg' ),
66                    'type'              => 'array',
67                    'required'          => false,
68                    'validate_callback' => function ( $param ) {
69                        return is_array( $param );
70                    },
71                    'sanitize_callback' => function ( $param ) {
72                        if ( ! is_array( $param ) ) {
73                            return new WP_Error(
74                                'rest_invalid_param',
75                                esc_html__( 'The skipped_connections argument must be an array of connection IDs.', 'jetpack-publicize-pkg' ),
76                                array( 'status' => 400 )
77                            );
78                        }
79                        return array_map( 'absint', $param );
80                    },
81                ),
82                'async'               => array(
83                    'description' => __( 'Whether to share the post asynchronously.', 'jetpack-publicize-pkg' ),
84                    'type'        => 'boolean',
85                    'default'     => false,
86                ),
87            ),
88        );
89
90        register_rest_route(
91            $this->namespace,
92            '/' . $this->rest_base . '/(?P<postId>\d+)',
93            $args
94        );
95
96        if ( Utils::is_wpcom() ) {
97            // WPCOM Legacy route for backwards compatibility.
98            // TODO: Remove this after April 2025 release of Jetpack.
99            register_rest_route(
100                $this->namespace,
101                '/posts/(?P<postId>\d+)/publicize',
102                $args
103            );
104        }
105    }
106
107    /**
108     * Ensure the user has proper tokens and permissions to publish posts on this blog.
109     *
110     * @return WP_Error|boolean
111     */
112    public function permissions_check() {
113        return $this->publicize_permissions_check();
114    }
115
116    /**
117     * If this method callback is executed on WPCOM, we share the post using republicize_post(). If this method callback
118     * is executed on a Jetpack site, we make an API call to WPCOM using wpcom_json_api_request_as_user() and return
119     * the results. In both cases, this file and method are executed, as this file is synced from Jetpack to WPCOM.
120     *
121     * @param  WP_REST_Request $request Full details about the request.
122     * @return WP_REST_Response|WP_Error The publicize results, including two arrays: `results` and `errors`
123     */
124    public function share_post( $request ) {
125        $post_id = $request->get_param( 'postId' );
126
127        if ( ! Utils::is_wpcom() ) {
128            return rest_ensure_response(
129                $this->proxy_request_to_wpcom_as_user( $request, $post_id )
130            );
131        }
132
133        $message             = trim( $request->get_param( 'message' ) );
134        $skip_connection_ids = $request->get_param( 'skipped_connections' );
135        $async               = (bool) $request->get_param( 'async' );
136        $post                = get_post( $post_id );
137
138        if ( empty( $post ) ) {
139            return new WP_Error( 'not_found', __( 'Cannot find that post.', 'jetpack-publicize-pkg' ), array( 'status' => 404 ) );
140        }
141        if ( 'publish' !== $post->post_status ) {
142            return new WP_Error( 'not_published', __( 'Only published posts can be shared.', 'jetpack-publicize-pkg' ), array( 'status' => 400 ) );
143        }
144
145        global $publicize;
146
147        // @phan-suppress-next-line PhanUndeclaredMethod - We are on WPCOM where republicize_post is available.
148        $result = $publicize->republicize_post( (int) $post_id, $message, $skip_connection_ids, true, ! $async, get_current_user_id() );
149        if ( false === $result ) {
150            return new WP_Error( 'not_found', __( 'Cannot find that post.', 'jetpack-publicize-pkg' ), array( 'status' => 404 ) );
151        }
152
153        return rest_ensure_response( $result );
154    }
155}