PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
jetpack / jetpack_vendor / automattic / jetpack-external-connections / src / class-external-connections.php

class-external-connections.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-beta, at jetpack_vendor/automattic/jetpack-external-connections/src/class-external-connections.php

347 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Management of external connections.
4 *
5 * @package automattic/jetpack-external-connections
6 */
7
8 namespace Automattic\Jetpack;
9
10 use Automattic\Jetpack\Connection\Client;
11 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
12 use Automattic\Jetpack\Status\Host;
13
14 /**
15 * Main class.
16 */
17 class External_Connections {
18
19 const PACKAGE_VERSION = '0.2.4';
20 const BASE_FILE = __FILE__;
21
22 /**
23 * List of services whose connections are managed in settings pages.
24 *
25 * Each item has a key with the slug of the settings page, and a value with an array of services.
26 *
27 * Each service has the following keys:
28 * - service: The service identifier.
29 * - title: The title of the service.
30 * - signup_link: The URL to the service's signup page.
31 * - description: The description of the service.
32 * - support_link: An array with the following keys:
33 * - jetpack: The URL handler registered in jetpack.com/redirect/.
34 * - wpcom: The URL of the support page for the service on WordPress.com.
35 * - script: An optional script handle to enqueue.
36 *
37 * @example
38 * ```php
39 * self::$services = array(
40 * 'media' => array(
41 * array(
42 * 'service' => 'facebook',
43 * 'title' => 'Facebook',
44 * 'signup_link' => 'https://facebook.com/signup?ref=jetpack',
45 * 'description' => 'Connect your site to your Facebook account',
46 * 'support_link' => array(
47 * 'jetpack' => 'facebook-connection',
48 * 'wpcom' => 'https://wordpress.com/support/facebook/',
49 * ),
50 * ),
51 * ),
52 * );
53 * ```
54 * @var array
55 */
56 private static $services = array();
57
58 /**
59 * Gets the connect URL for a given service.
60 *
61 * @param string $service The service identifier.
62 * @return string|null The connect URL, or `null` if the service is not supported.
63 */
64 public static function get_connect_url( $service ) {
65 if ( ( new Host() )->is_wpcom_simple() ) {
66 require_lib( 'external-connections' );
67 $connections = \WPCOM_External_Connections::init();
68 $service = $connections->get_external_service_item( $service );
69 return $service ? $service['connect_URL'] : null;
70 }
71
72 $site_id = Connection_Manager::get_site_id();
73 if ( is_wp_error( $site_id ) ) {
74 return null;
75 }
76
77 $path = sprintf( '/sites/%d/external-services', $site_id );
78 $response = Client::wpcom_json_api_request_as_user( $path );
79 if ( is_wp_error( $response ) ) {
80 return null;
81 }
82
83 $body = json_decode( wp_remote_retrieve_body( $response ) );
84
85 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
86 return $body->services->$service->connect_URL ?? null;
87 }
88
89 /**
90 * Retrieves a connection of the provided service.
91 *
92 * @param string $service The service identifier.
93 * @return array|null The connection details, or `null` if no matching connection is found.
94 */
95 public static function get_connection( $service ) {
96 if ( ( new Host() )->is_wpcom_simple() ) {
97 require_lib( 'external-media-service/external-media-list' );
98 require_lib( 'external-connections' );
99
100 $connections = \WPCOM_External_Connections::init();
101 $token = \ExternalMediaService::get_service_token( $service, get_current_user_id() );
102
103 if ( ! empty( $token->unique_id ) ) {
104 // @phan-suppress-next-line PhanTypeMismatchArgument -- $token->unique_id is an int.
105 return $connections->get_keyring_connection_item( $token->unique_id );
106 }
107 } else {
108 $response = Client::wpcom_json_api_request_as_user( '/me/connections' );
109 if ( is_wp_error( $response ) ) {
110 return null;
111 }
112 $body = json_decode( wp_remote_retrieve_body( $response ) );
113 if ( isset( $body->connections ) && is_array( $body->connections ) ) {
114 foreach ( $body->connections as $connection ) {
115 if ( $service === $connection->service ) {
116 return (array) $connection;
117 }
118 }
119 }
120 }
121
122 return null;
123 }
124
125 /**
126 * Deletes a connection for the provided service.
127 *
128 * @param string $service The service identifier.
129 * @return boolean Whether the connection was deleted.
130 */
131 public static function delete_connection( $service ) {
132 $connection = self::get_connection( $service );
133 if ( empty( $connection ) ) {
134 return false;
135 }
136
137 if ( ( new Host() )->is_wpcom_simple() ) {
138 if ( get_current_user_id() === $connection['user_ID'] ) {
139 require_lib( 'external-connections' );
140 $connections = \WPCOM_External_Connections::init();
141 return $connections->delete_keyring_connection( $connection['ID'] );
142 }
143 return false;
144 }
145
146 $response = Client::wpcom_json_api_request_as_user(
147 '/me/connections/' . $connection['ID'],
148 '2',
149 array( 'method' => 'DELETE' )
150 );
151 if ( is_wp_error( $response ) ) {
152 return false;
153 }
154 $body = json_decode( wp_remote_retrieve_body( $response ) );
155 return $body->deleted ?? false;
156 }
157
158 /**
159 * Gets the connection data for the provided service.
160 *
161 * @param string $service The service identifier.
162 * @return array The connection data.
163 */
164 public static function get_connection_data( $service ) {
165 $connection = self::get_connection( $service );
166 if ( empty( $connection ) ) {
167 return array(
168 'is_connected' => false,
169 'account_name' => '',
170 'profile_image' => '',
171 );
172 }
173
174 $is_connected = isset( $connection['status'] ) && $connection['status'] === 'ok';
175 $account_name = $is_connected ? ( $connection['external_display'] ?? $connection['external_name'] ) : '';
176 $profile_image = $is_connected ? $connection['external_profile_picture'] : '';
177
178 return array(
179 'is_connected' => $is_connected,
180 'account_name' => $account_name,
181 'profile_image' => $profile_image,
182 );
183 }
184
185 /**
186 * Registers connection settings.
187 */
188 public static function register_settings() {
189 global $pagenow;
190 $host = new Host();
191
192 if ( ! $host->is_wpcom_simple() ) {
193 $connection = new Connection_Manager( 'jetpack' );
194 $status = new Status();
195
196 if ( $status->is_offline_mode() || ! $connection->has_connected_owner() || ! $connection->is_user_connected() ) {
197 return;
198 }
199 }
200
201 foreach ( self::$services as $page => $services ) {
202 if ( $pagenow !== "options-$page.php" ) {
203 continue;
204 }
205
206 add_settings_section(
207 'jetpack_external_connections_section',
208 __( 'Integrations', 'jetpack-external-connections' ),
209 '__return_false',
210 $page
211 );
212
213 $asset_name = 'jetpack-external-connections-settings';
214 Assets::register_script(
215 $asset_name,
216 "build/$asset_name/$asset_name.js",
217 self::BASE_FILE,
218 array(
219 'in_footer' => true,
220 'textdomain' => 'jetpack-external-connections',
221 )
222 );
223 Assets::enqueue_script( $asset_name );
224
225 $script_data = array();
226
227 foreach ( $services as $service ) {
228 if ( $host->is_wpcom_platform() ) {
229 $support_link = $service['support_link']['wpcom'];
230 if ( function_exists( 'localized_wpcom_url' ) ) {
231 $support_link = localized_wpcom_url( $support_link );
232 }
233 } else {
234 $support_link = Redirect::get_url( $service['support_link']['jetpack'] );
235 }
236
237 $connect_url = self::get_connect_url( $service['service'] );
238 $connection_data = self::get_connection_data( $service['service'] );
239
240 if ( ( empty( $connect_url ) && ! $connection_data['is_connected'] ) ) {
241 continue;
242 }
243
244 add_settings_field(
245 'jetpack_external_connections_field_' . $service['service'],
246 $service['title'],
247 function () use ( $service, $support_link ) {
248 ?>
249 <div class="jetpack-external-connection" data-service="<?php echo esc_attr( $service['service'] ); ?>"><em><?php esc_html_e( 'Loading…', 'jetpack-external-connections' ); ?></em></div>
250 <p class="description">
251 <?php echo esc_html( $service['description'] ); ?>
252 <a href="<?php echo esc_url( $support_link ); ?>" target="_blank" data-target="wpcom-help-center"><?php esc_html_e( 'Learn more', 'jetpack-external-connections' ); ?></a>
253 </p>
254 <?php
255 },
256 $page,
257 'jetpack_external_connections_section'
258 );
259
260 $script_data[ $service['service'] ] = array(
261 'accountName' => $connection_data['account_name'],
262 'connectUrl' => $connect_url,
263 'deleteNonce' => wp_create_nonce( 'jetpack_delete_external_connection_' . $service['service'] ),
264 'getNonce' => wp_create_nonce( 'jetpack_get_external_connection_' . $service['service'] ),
265 'isConnected' => $connection_data['is_connected'],
266 'profileImage' => $connection_data['profile_image'],
267 'supportLink' => $support_link,
268 'signupLink' => $service['signup_link'] ?? '',
269 );
270
271 if ( isset( $service['script'] ) ) {
272 Assets::enqueue_script( $service['script'] );
273 }
274 }
275
276 wp_add_inline_script(
277 $asset_name,
278 'const jetpackExternalConnectionsData = ' . wp_json_encode( $script_data, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';',
279 'before'
280 );
281 }
282 }
283
284 /**
285 * Handles the AJAX request to delete an external connection.
286 */
287 public static function ajax_delete_connection() {
288 if ( ! isset( $_REQUEST['service'] ) ) {
289 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
290 wp_send_json( array( 'deleted' => 'false' ), null, JSON_UNESCAPED_SLASHES );
291 }
292
293 $service = sanitize_text_field( wp_unslash( $_REQUEST['service'] ) );
294 check_ajax_referer( 'jetpack_delete_external_connection_' . $service );
295
296 $is_deleted = self::delete_connection( $service );
297 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
298 wp_send_json( array( 'deleted' => $is_deleted ), null, JSON_UNESCAPED_SLASHES );
299 }
300
301 /**
302 * Handles the AJAX request to delete an external connection.
303 */
304 public static function ajax_get_connection() {
305 if ( ! isset( $_REQUEST['service'] ) ) {
306 // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
307 wp_send_json( array( 'isConnected' => 'false' ), null, JSON_UNESCAPED_SLASHES );
308 }
309
310 $service = sanitize_text_field( wp_unslash( $_REQUEST['service'] ) );
311 check_ajax_referer( 'jetpack_get_external_connection_' . $service );
312
313 $connection_data = self::get_connection_data( $service );
314 wp_send_json(
315 array(
316 'accountName' => $connection_data['account_name'],
317 'isConnected' => $connection_data['is_connected'],
318 'profileImage' => $connection_data['profile_image'],
319 ),
320 null, // @phan-suppress-current-line PhanTypeMismatchArgumentProbablyReal -- It takes null, but its phpdoc only says int.
321 JSON_UNESCAPED_SLASHES
322 );
323 }
324
325 /**
326 * Registers settings and hooks for a specified service on a given admin page.
327 *
328 * @param string $page The identifier of the admin page where the service settings are added.
329 * @param array $service The service to be associated with the specified admin page.
330 */
331 public static function add_settings_for_service( $page, $service ) {
332 self::$services[ $page ][] = $service;
333
334 if ( ! has_action( 'admin_init', array( __CLASS__, 'register_settings' ) ) ) {
335 add_action( 'admin_init', array( __CLASS__, 'register_settings' ), 15 );
336 }
337
338 if ( ! has_action( 'wp_ajax_jetpack_delete_external_connection', array( __CLASS__, 'ajax_delete_connection' ) ) ) {
339 add_action( 'wp_ajax_jetpack_delete_external_connection', array( __CLASS__, 'ajax_delete_connection' ) );
340 }
341
342 if ( ! has_action( 'wp_ajax_jetpack_get_external_connection', array( __CLASS__, 'ajax_get_connection' ) ) ) {
343 add_action( 'wp_ajax_jetpack_get_external_connection', array( __CLASS__, 'ajax_get_connection' ) );
344 }
345 }
346 }
347