Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
7.79% covered (danger)
7.79%
6 / 77
13.33% covered (danger)
13.33%
2 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
Connection
7.79% covered (danger)
7.79%
6 / 77
13.33% covered (danger)
13.33%
2 / 15
735.58
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 init
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 initialize_deactivate_disconnect
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 deactivate_disconnect
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 deactivate_disconnect_network
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 wpcom_blog_id
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
12
 is_connected
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 register
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 disconnect
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 register_rest_routes
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
2
 create_connection_endpoint
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 get_connection_endpoint
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_connection_api_response
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 can_manage_connection
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 rest_authorization_required_code
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Jetpack connection client.
4 *
5 * @link       https://automattic.com
6 * @since      1.0.0
7 * @package    automattic/jetpack-boost
8 */
9
10namespace Automattic\Jetpack_Boost\Lib;
11
12use Automattic\Jetpack\Connection\Manager;
13use Automattic\Jetpack\Terms_Of_Service;
14
15/**
16 * Class Connection
17 *
18 * Manages the Jetpack connection on behalf of Jetpack Boost.
19 */
20class Connection {
21
22    /**
23     * Jetpack Connection Manager.
24     *
25     * @var Manager $manager The connection manager.
26     */
27    private $manager;
28
29    public function __construct() {
30        $this->manager = new Manager( 'jetpack-boost' );
31    }
32
33    public function init() {
34        add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
35
36        $this->initialize_deactivate_disconnect();
37    }
38
39    /**
40     * Initialize the plugin deactivation hook.
41     */
42    public function initialize_deactivate_disconnect() {
43        require_once ABSPATH . '/wp-admin/includes/plugin.php';
44
45        if ( is_plugin_active_for_network( JETPACK_BOOST_PATH ) ) {
46            register_deactivation_hook( JETPACK_BOOST_PATH, array( $this, 'deactivate_disconnect_network' ) );
47        } else {
48            register_deactivation_hook( JETPACK_BOOST_PATH, array( $this, 'deactivate_disconnect' ) );
49        }
50    }
51
52    /**
53     * Deactivate the connection on plugin disconnect.
54     */
55    public function deactivate_disconnect() {
56        $this->manager->remove_connection();
57    }
58
59    /**
60     * Deactivate the connection on plugin disconnect for network-activated plugins.
61     */
62    public function deactivate_disconnect_network() {
63        if ( ! is_network_admin() ) {
64            return;
65        }
66
67        foreach ( get_sites() as $s ) {
68            switch_to_blog( (int) $s->blog_id );
69
70            $active_plugins = get_option( 'active_plugins' );
71
72            /*
73             * If this plugin was activated in the subsite individually
74             * we do not want to call disconnect. Plugins activated
75             * individually (before network activation) stay activated
76             * when the network deactivation occurs
77             */
78            if ( ! in_array( JETPACK_BOOST_PATH, $active_plugins, true ) ) {
79                $this->deactivate_disconnect();
80            }
81
82            restore_current_blog();
83        }
84    }
85
86    /**
87     * Connection Lifecycle methods.
88     */
89
90    /**
91     * Get the WordPress.com blog ID of this site, if it's connected
92     */
93    public static function wpcom_blog_id() {
94        return defined( 'IS_WPCOM' ) && IS_WPCOM ? get_current_blog_id() : (int) \Jetpack_Options::get_option( 'id' );
95    }
96
97    /**
98     * True if the site is connected to WP.com.
99     *
100     * @return boolean
101     */
102    public function is_connected() {
103        /**
104         * Filter that fakes the connection to WordPress.com. Useful for testing.
105         *
106         * @param bool $connection Return true to fake the connection.
107         *
108         * @since   1.0.0
109         */
110        if ( true === apply_filters( 'jetpack_boost_connection_bypass', false ) ) {
111            return true;
112        }
113
114        return $this->manager->is_connected();
115    }
116
117    /**
118     * Register site using connection manager.
119     *
120     * @return true|\WP_Error The error object.
121     */
122    public function register() {
123        if ( $this->is_connected() ) {
124            Analytics::record_user_event( 'using_existing_connection' );
125            return true;
126        }
127
128        $result = $this->manager->register();
129
130        if ( ! is_wp_error( $result ) ) {
131            Analytics::record_user_event( 'established_connection' );
132            Premium_Features::clear_cache();
133        }
134
135        return $result;
136    }
137
138    /**
139     * Disconnect from Jetpack account.
140     *
141     * @return bool
142     */
143    public function disconnect() {
144        // @todo implement check for Jetpack::validate_sync_error_idc_option() so we don't disconnect production site from staging etc.
145        Analytics::record_user_event( 'disconnect_site' );
146
147        $this->manager->remove_connection();
148
149        return true;
150    }
151
152    /**
153     * REST endpoint methods.
154     */
155    public function register_rest_routes() {
156        register_rest_route(
157            JETPACK_BOOST_REST_NAMESPACE,
158            JETPACK_BOOST_REST_PREFIX . '/connection',
159            array(
160                'methods'             => \WP_REST_Server::READABLE,
161                'callback'            => array( $this, 'get_connection_endpoint' ),
162                'permission_callback' => array( $this, 'can_manage_connection' ),
163            )
164        );
165
166        register_rest_route(
167            JETPACK_BOOST_REST_NAMESPACE,
168            JETPACK_BOOST_REST_PREFIX . '/connection',
169            array(
170                'methods'             => \WP_REST_Server::EDITABLE,
171                'callback'            => array( $this, 'create_connection_endpoint' ),
172                'permission_callback' => array( $this, 'can_manage_connection' ),
173            )
174        );
175    }
176
177    /**
178     * Register site using connection manager.
179     *
180     * @param \WP_REST_Request $request The request object.
181     *
182     * @return \WP_REST_Response|\WP_Error
183     */
184    public function create_connection_endpoint( \WP_REST_Request $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
185
186        ( new Terms_Of_Service() )->agree();
187
188        $response = $this->register();
189
190        // Clear premium features cache to force a refresh.
191        Premium_Features::clear_cache();
192
193        if ( is_wp_error( $response ) ) {
194            return $response;
195        }
196
197        do_action( 'jetpack_boost_connection_established' );
198
199        return rest_ensure_response( $this->get_connection_api_response() );
200    }
201
202    /**
203     * Fetch connection info.
204     *
205     * @param \WP_REST_Request $request The request object.
206     *
207     * @return \WP_REST_Response|\WP_Error
208     */
209    public function get_connection_endpoint( \WP_REST_Request $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
210        return rest_ensure_response( $this->get_connection_api_response() );
211    }
212
213    /**
214     * Connection state sent to client on initialization and after updates.
215     *
216     * @return array
217     */
218    public function get_connection_api_response() {
219        /**
220         * Filter that fakes the connection to WordPress.com. Useful for testing.
221         *
222         * @param bool $connection Return true to fake the connection.
223         *
224         * @since   1.0.0
225         */
226        $force_connected = apply_filters( 'jetpack_boost_connection_bypass', false );
227
228        $response = array(
229            'connected'     => $force_connected || $this->is_connected(),
230            'wpcomBlogId'   => ( $force_connected || $this->is_connected() ) ? self::wpcom_blog_id() : null,
231            'userConnected' => $this->manager->is_user_connected(),
232        );
233
234        return $response;
235    }
236
237    /**
238     * Can user manage the connection?
239     *
240     * @return boolean | \WP_Error
241     */
242    public function can_manage_connection() {
243        if ( current_user_can( 'manage_options' ) ) {
244            return true;
245        }
246
247        $user_permissions_error_msg = __(
248            'You do not have the correct user permissions to perform this action.
249            Please contact your site admin if you think this is a mistake.',
250            'jetpack-boost'
251        );
252
253        return new \WP_Error(
254            'invalid_user_permission_jetpack_connect',
255            $user_permissions_error_msg,
256            array( 'status' => self::rest_authorization_required_code() )
257        );
258    }
259
260    /**
261     * Contextual HTTP error code for authorization failure.
262     *
263     * Taken from rest_authorization_required_code() in WP-API plugin until is added to core.
264     *
265     * @see   https://github.com/WP-API/WP-API/commit/7ba0ae6fe4f605d5ffe4ee85b1cd5f9fb46900a6
266     *
267     * @since 4.3.0
268     *
269     * @return int
270     */
271    public static function rest_authorization_required_code() {
272        return is_user_logged_in() ? 403 : 401;
273    }
274}