Completed
Push — add/connection-ui ( 776f55...bf4f6b )
by
unknown
184:54 queued 174:43
created

REST_Connector::connect_disconnect_plugin()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 6
nop 1
dl 0
loc 21
rs 8.9617
c 0
b 0
f 0
1
<?php
2
/**
3
 * Sets up the Connection REST API endpoints.
4
 *
5
 * @package automattic/jetpack-connection
6
 */
7
8
namespace Automattic\Jetpack\Connection;
9
10
use Automattic\Jetpack\Status;
11
use Jetpack_XMLRPC_Server;
12
use WP_Error;
13
use WP_REST_Request;
14
use WP_REST_Response;
15
use WP_REST_Server;
16
17
/**
18
 * Registers the REST routes for Connections.
19
 */
20
class REST_Connector {
21
	/**
22
	 * The Connection Manager.
23
	 *
24
	 * @var Manager
25
	 */
26
	private $connection;
27
28
	/**
29
	 * This property stores the localized "Insufficient Permissions" error message.
30
	 *
31
	 * @var string Generic error message when user is not allowed to perform an action.
32
	 */
33
	private static $user_permissions_error_msg;
34
35
	/**
36
	 * Constructor.
37
	 *
38
	 * @param Manager $connection The Connection Manager.
39
	 */
40
	public function __construct( Manager $connection ) {
41
		$this->connection = $connection;
42
43
		self::$user_permissions_error_msg = esc_html__(
44
			'You do not have the correct user permissions to perform this action.
45
			Please contact your site admin if you think this is a mistake.',
46
			'jetpack'
47
		);
48
49
		if ( ! $this->connection->is_active() ) {
50
			// Register a site.
51
			register_rest_route(
52
				'jetpack/v4',
53
				'/verify_registration',
54
				array(
55
					'methods'             => WP_REST_Server::EDITABLE,
56
					'callback'            => array( $this, 'verify_registration' ),
57
					'permission_callback' => '__return_true',
58
				)
59
			);
60
		}
61
62
		// Authorize a remote user.
63
		register_rest_route(
64
			'jetpack/v4',
65
			'/remote_authorize',
66
			array(
67
				'methods'             => WP_REST_Server::EDITABLE,
68
				'callback'            => __CLASS__ . '::remote_authorize',
69
				'permission_callback' => '__return_true',
70
			)
71
		);
72
73
		// Get current connection status of Jetpack.
74
		register_rest_route(
75
			'jetpack/v4',
76
			'/connection',
77
			array(
78
				'methods'             => WP_REST_Server::READABLE,
79
				'callback'            => __CLASS__ . '::connection_status',
80
				'permission_callback' => '__return_true',
81
			)
82
		);
83
84
		// Get list of plugins that use the Jetpack connection.
85
		register_rest_route(
86
			'jetpack/v4',
87
			'/connection/plugins',
88
			array(
89
				'methods'             => WP_REST_Server::READABLE,
90
				'callback'            => array( $this, 'get_connection_plugins' ),
91
				'permission_callback' => __CLASS__ . '::activate_plugins_permission_check',
92
			)
93
		);
94
95
		// Full or partial reconnect in case of connection issues.
96
		register_rest_route(
97
			'jetpack/v4',
98
			'/connection/reconnect',
99
			array(
100
				'methods'             => WP_REST_Server::EDITABLE,
101
				'callback'            => array( $this, 'connection_reconnect' ),
102
				'permission_callback' => __CLASS__ . '::jetpack_reconnect_permission_check',
103
				'args'                => array(
104
					'from' => array( 'type' => 'string' ),
105
				),
106
			)
107
		);
108
109
		// Softly disconnect or reconnect a plugin.
110
		register_rest_route(
111
			'jetpack/v4',
112
			'/connection/connect_disconnect_plugin',
113
			array(
114
				'methods'             => WP_REST_Server::EDITABLE,
115
				'callback'            => array( $this, 'connect_disconnect_plugin' ),
116
				'permission_callback' => __CLASS__ . '::activate_plugins_permission_check',
117
				'args'                => array(
118
					'slug'       => array( 'type' => 'string' ),
119
					'connect'    => array( 'type' => 'boolean' ),
120
					'disconnect' => array( 'type' => 'boolean' ),
121
				),
122
			)
123
		);
124
	}
125
126
	/**
127
	 * Handles verification that a site is registered.
128
	 *
129
	 * @since 5.4.0
130
	 *
131
	 * @param WP_REST_Request $request The request sent to the WP REST API.
132
	 *
133
	 * @return string|WP_Error
134
	 */
135
	public function verify_registration( WP_REST_Request $request ) {
136
		$registration_data = array( $request['secret_1'], $request['state'] );
137
138
		return $this->connection->handle_registration( $registration_data );
139
	}
140
141
	/**
142
	 * Handles verification that a site is registered
143
	 *
144
	 * @since 5.4.0
145
	 *
146
	 * @param WP_REST_Request $request The request sent to the WP REST API.
147
	 *
148
	 * @return array|wp-error
0 ignored issues
show
Documentation introduced by
The doc-type array|wp-error could not be parsed: Unknown type name "wp-error" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
149
	 */
150
	public static function remote_authorize( $request ) {
151
		$xmlrpc_server = new Jetpack_XMLRPC_Server();
152
		$result        = $xmlrpc_server->remote_authorize( $request );
153
154
		if ( is_a( $result, 'IXR_Error' ) ) {
155
			$result = new WP_Error( $result->code, $result->message );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with $result->code.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
156
		}
157
158
		return $result;
159
	}
160
161
	/**
162
	 * Get connection status for this Jetpack site.
163
	 *
164
	 * @since 4.3.0
165
	 *
166
	 * @param bool $rest_response Should we return a rest response or a simple array. Default to rest response.
167
	 *
168
	 * @return WP_REST_Response|array Connection information.
169
	 */
170
	public static function connection_status( $rest_response = true ) {
171
		$status     = new Status();
172
		$connection = new Manager();
173
174
		$connection_status = array(
175
			'isActive'     => $connection->is_active(),
176
			'isStaging'    => $status->is_staging_site(),
177
			'isRegistered' => $connection->is_connected(),
178
			'offlineMode'  => array(
179
				'isActive'        => $status->is_offline_mode(),
180
				'constant'        => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,
181
				'url'             => $status->is_local_site(),
182
				/** This filter is documented in packages/status/src/class-status.php */
183
				'filter'          => ( apply_filters( 'jetpack_development_mode', false ) || apply_filters( 'jetpack_offline_mode', false ) ), // jetpack_development_mode is deprecated.
184
				'wpLocalConstant' => defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV,
185
			),
186
			'isPublic'     => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
187
		);
188
189
		if ( $rest_response ) {
190
			return rest_ensure_response(
191
				$connection_status
192
			);
193
		} else {
194
			return $connection_status;
195
		}
196
	}
197
198
	/**
199
	 * Get plugins connected to the Jetpack.
200
	 *
201
	 * @since 8.6.0
202
	 *
203
	 * @return WP_REST_Response|WP_Error Response or error object, depending on the request result.
204
	 */
205
	public function get_connection_plugins() {
206
		$plugins = $this->connection->get_connected_plugins();
207
208
		if ( is_wp_error( $plugins ) ) {
209
			return $plugins;
210
		}
211
212
		array_walk(
213
			$plugins,
214
			function ( &$data, $slug ) {
215
				$data['slug'] = $slug;
216
			}
217
		);
218
219
		return rest_ensure_response( array_values( $plugins ) );
220
	}
221
222
	/**
223
	 * Verify that user can view Jetpack admin page and can activate plugins.
224
	 *
225
	 * @since 8.8.0
226
	 *
227
	 * @return bool|WP_Error Whether user has the capability 'activate_plugins'.
228
	 */
229
	public static function activate_plugins_permission_check() {
230
		if ( current_user_can( 'activate_plugins' ) ) {
231
			return true;
232
		}
233
234
		return new WP_Error( 'invalid_user_permission_activate_plugins', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_user_permission_activate_plugins'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
235
	}
236
237
	/**
238
	 * Verify that user is allowed to disconnect Jetpack.
239
	 *
240
	 * @since 8.8.0
241
	 *
242
	 * @return bool|WP_Error Whether user has the capability 'jetpack_disconnect'.
243
	 */
244
	public static function jetpack_reconnect_permission_check() {
245
		if ( current_user_can( 'jetpack_reconnect' ) ) {
246
			return true;
247
		}
248
249
		return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_user_permission_jetpack_disconnect'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
250
	}
251
252
	/**
253
	 * Returns generic error message when user is not allowed to perform an action.
254
	 *
255
	 * @return string The error message.
256
	 */
257
	public static function get_user_permissions_error_msg() {
258
		return self::$user_permissions_error_msg;
259
	}
260
261
	/**
262
	 * The endpoint tried to partially or fully reconnect the website to WP.com.
263
	 *
264
	 * @param WP_REST_Request|\ArrayAccess|array $request The request sent to the WP REST API.
265
	 *
266
	 * @since 8.8.0
267
	 *
268
	 * @return \WP_REST_Response|WP_Error
269
	 */
270
	public function connection_reconnect( $request = array() ) {
271
		$response = array();
272
273
		$next = null;
274
275
		$result = $this->connection->restore();
276
277
		if ( is_wp_error( $result ) ) {
278
			$response = $result;
279
		} elseif ( is_string( $result ) ) {
280
			$next = $result;
281
		} else {
282
			$next = true === $result ? 'completed' : 'failed';
283
		}
284
285
		switch ( $next ) {
286
			case 'authorize':
287
				$redirect_url = null;
288
289
				if ( ! empty( $request['from'] ) && 'connection-ui' === $request['from'] ) {
290
					$redirect_url = admin_url( 'tools.php?page=wpcom-connection-manager#refreshed' );
291
				}
292
293
				$response['status']       = 'in_progress';
294
				$response['authorizeUrl'] = $this->connection->get_authorization_url( null, $redirect_url );
295
296
				if ( $redirect_url ) {
297
					$response['authorizeUrl'] = add_query_arg( 'from', $request['from'], $response['authorizeUrl'] );
298
				}
299
300
				break;
301
			case 'completed':
302
				$response['status'] = 'completed';
303
				/**
304
				 * Action fired when reconnection has completed successfully.
305
				 *
306
				 * @since 9.0.0
307
				 */
308
				do_action( 'jetpack_reconnection_completed' );
309
				break;
310
			case 'failed':
311
				$response = new WP_Error( 'Reconnect failed' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'Reconnect failed'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
312
				break;
313
		}
314
315
		return rest_ensure_response( $response );
316
	}
317
318
	/**
319
	 * Softly disconnect or reconnect a plugin.
320
	 *
321
	 * @param WP_REST_Request|\ArrayAccess|array $request The request sent to the WP REST API.
322
	 *
323
	 * @return WP_Error|\WP_HTTP_Response|WP_REST_Response
324
	 */
325
	public function connect_disconnect_plugin( $request = array() ) {
326
		if ( empty( $request['slug'] ) ) {
327
			return new WP_Error( 'Plugin slug is required' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'Plugin slug is required'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
328
		}
329
330
		if ( empty( $request['connect'] ) && empty( $request['disconnect'] ) ) {
331
			return new WP_Error( "The 'connect' or 'disconnect' flag is required." );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'The \'connect\' or \'di...ct\' flag is required.'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
332
		}
333
334
		$plugin = new Plugin( $request['slug'] );
335
336
		if ( $request['connect'] ) {
337
			$plugin->enable();
338
		}
339
340
		if ( $request['disconnect'] ) {
341
			$plugin->disable();
342
		}
343
344
		return rest_ensure_response( array( 'success' => true ) );
345
	}
346
347
}
348