PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.1-a.5
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-external-connections / src / class-external-connections.php
jetpack / jetpack_vendor / automattic / jetpack-external-connections / src Last commit date
build 3 months ago class-external-connections.php 1 month ago
class-external-connections.php
347 lines
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.1.38';
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