PluginProbe
Elementor Website Builder – more than just a page builder / 4.2.4
Elementor Website Builder – more than just a page builder v4.2.4
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / core / common / modules / connect / apps / base-app.php

base-app.php in Elementor Website Builder – more than just a page builder 4.2.4, at core/common/modules/connect/apps/base-app.php

864 lines 20.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Core\Common\Modules\Connect\Apps;
3
4 use Elementor\Core\Admin\Admin_Notices;
5 use Elementor\Core\Common\Modules\Connect\Admin;
6 use Elementor\Core\Utils\Collection;
7 use Elementor\Core\Utils\Http;
8 use Elementor\Core\Utils\Str;
9 use Elementor\Plugin;
10 use Elementor\Tracker;
11 use Elementor\Utils;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly.
15 }
16
17 abstract class Base_App {
18
19 const OPTION_NAME_PREFIX = 'elementor_connect_';
20
21 const OPTION_CONNECT_SITE_KEY = self::OPTION_NAME_PREFIX . 'site_key';
22
23 const SITE_URL = 'https://my.elementor.com/connect/v1';
24
25 const API_URL = 'https://my.elementor.com/api/connect/v1';
26
27 const HTTP_RETURN_TYPE_OBJECT = 'object';
28 const HTTP_RETURN_TYPE_ARRAY = 'array';
29
30 protected $data = [];
31
32 protected $auth_mode = '';
33
34 /**
35 * @var Http
36 */
37 protected $http;
38
39 /**
40 * @since 2.3.0
41 * @access protected
42 * @abstract
43 * TODO: make it public.
44 */
45 abstract protected function get_slug();
46
47 /**
48 * @since 2.8.0
49 * @access public
50 * TODO: make it abstract.
51 */
52 public function get_title() {
53 return $this->get_slug();
54 }
55
56 /**
57 * @since 2.3.0
58 * @access protected
59 * @abstract
60 */
61 abstract protected function update_settings();
62
63 /**
64 * @since 2.3.0
65 * @access public
66 * @static
67 */
68 public static function get_class_name() {
69 return get_called_class();
70 }
71
72 /**
73 * @access public
74 * @abstract
75 */
76 public function render_admin_widget() {
77 // PHPCS - the method get_title return a plain string.
78 echo '<h2>' . $this->get_title() . '</h2>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
79
80 if ( $this->is_connected() ) {
81 $remote_user = $this->get( 'user' );
82 $title = sprintf(
83 /* translators: %s: Remote user. */
84 esc_html__( 'Connected as %s', 'elementor' ),
85 '<strong>' . esc_html( $remote_user->email ) . '</strong>'
86 );
87 $label = esc_html__( 'Disconnect', 'elementor' );
88 $url = $this->get_admin_url( 'disconnect' );
89 $attr = '';
90
91 printf(
92 '%s <a %s href="%s">%s</a>',
93 // PHPCS - the variable $title is already escaped above.
94 $title, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
95 // PHPCS - the variable $attr is a plain string.
96 $attr, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
97 esc_attr( $url ),
98 esc_html( $label )
99 );
100 } else {
101 echo 'Not Connected';
102 }
103
104 echo '<hr>';
105
106 $this->print_app_info();
107
108 if ( current_user_can( 'manage_options' ) ) {
109 printf( '<div><a href="%s">%s</a></div>', esc_url( $this->get_admin_url( 'reset' ) ), esc_html__( 'Reset Data', 'elementor' ) );
110 }
111
112 echo '<hr>';
113 }
114
115
116 /**
117 * @since 2.3.0
118 * @access protected
119 */
120 protected function get_option_name() {
121 return static::OPTION_NAME_PREFIX . $this->get_slug();
122 }
123
124 /**
125 * @since 2.3.0
126 * @access public
127 */
128 public function admin_notice() {
129 $notices = $this->get( 'notices' );
130
131 if ( ! $notices ) {
132 return;
133 }
134
135 $this->print_notices( $notices );
136
137 $this->delete( 'notices' );
138 }
139
140
141 public function get_app_token_from_cli_token( $cli_token ) {
142 $response = $this->request( 'get_app_token_from_cli_token', [
143 'cli_token' => $cli_token,
144 ] );
145
146 if ( is_wp_error( $response ) ) {
147 // PHPCS - the variable $response does not contain a user input value.
148 wp_die( $response, $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
149 }
150
151 // Use state as usual.
152 $_REQUEST['state'] = $this->get( 'state' );
153 $_REQUEST['code'] = $response->code;
154 }
155 /**
156 * @since 2.3.0
157 * @access public
158 */
159 public function action_authorize() {
160 if ( $this->is_connected() ) {
161 $this->add_notice( esc_html__( 'Already connected.', 'elementor' ), 'info' );
162 $this->redirect_to_admin_page();
163 return;
164 }
165
166 $this->set_client_id();
167 $this->set_request_state();
168
169 $this->redirect_to_remote_authorize_url();
170 }
171
172 public function action_reset() {
173 $this->redirect_to_admin_page();
174 }
175
176 /**
177 * @since 2.3.0
178 * @access public
179 */
180 public function action_get_token() {
181 if ( $this->is_connected() ) {
182 $this->redirect_to_admin_page();
183 }
184
185 //phpcs:ignore WordPress.Security.NonceVerification.Recommended - The user as been authorized before in 'connect'.
186 $state = Utils::get_super_global_value( $_REQUEST, 'state' );
187
188 if ( $state !== $this->get( 'state' ) ) {
189 $this->add_notice( 'Get Token: Invalid Request.', 'error' );
190 $this->redirect_to_admin_page();
191 }
192
193 $response = $this->request( 'get_token', [
194 'grant_type' => 'authorization_code',
195 'code' => Utils::get_super_global_value( $_REQUEST, 'code' ), //phpcs:ignore WordPress.Security.NonceVerification.Recommended
196 'redirect_uri' => rawurlencode( $this->get_admin_url( 'get_token' ) ),
197 'client_id' => $this->get( 'client_id' ),
198 ] );
199
200 if ( is_wp_error( $response ) ) {
201 $notice = 'Cannot Get Token:' . $response->get_error_message();
202 $this->add_notice( $notice, 'error' );
203 $this->redirect_to_admin_page();
204 }
205
206 $this->delete( 'state' );
207 $this->set( (array) $response );
208
209 if ( ! empty( $response->data_share_opted_in ) && current_user_can( 'manage_options' ) ) {
210 Tracker::set_opt_in( true );
211 }
212
213 $this->after_connect();
214
215 // Add the notice *after* the method `after_connect`, so an app can redirect without the notice.
216 $this->add_notice( esc_html__( 'Connected successfully.', 'elementor' ) );
217
218 $this->redirect_to_admin_page();
219 }
220
221 /**
222 * @since 2.3.0
223 * @access public
224 */
225 public function action_disconnect() {
226 if ( $this->is_connected() ) {
227 $this->disconnect();
228 $this->add_notice( esc_html__( 'Disconnected successfully.', 'elementor' ) );
229 }
230
231 $this->redirect_to_admin_page();
232 }
233
234 /**
235 * @since 2.8.0
236 * @access public
237 */
238 public function action_reconnect() {
239 $this->disconnect();
240
241 $this->action_authorize();
242 }
243
244 /**
245 * @since 2.3.0
246 * @access public
247 */
248 public function get_admin_url( $action, $params = [] ) {
249 $params = [
250 'app' => $this->get_slug(),
251 'action' => $action,
252 'nonce' => wp_create_nonce( $this->get_slug() . $action ),
253 ] + $params;
254
255 $admin_url = Str::encode_idn_url( get_admin_url() );
256 $admin_url .= 'admin.php?page=' . Admin::PAGE_ID;
257
258 return add_query_arg( $params, $admin_url );
259 }
260
261 /**
262 * @since 2.3.0
263 * @access public
264 */
265 public function is_connected() {
266 return (bool) $this->get( 'access_token' );
267 }
268
269 /**
270 * @since 2.3.0
271 * @access protected
272 */
273 protected function init() {}
274
275 /**
276 * @since 2.3.0
277 * @access protected
278 */
279 protected function init_data() {}
280
281 /**
282 * @since 2.3.0
283 * @access protected
284 */
285 protected function after_connect() {}
286
287 /**
288 * @since 2.3.0
289 * @access public
290 */
291 public function get( $key, $default_value = null ) {
292 $this->init_data();
293
294 return isset( $this->data[ $key ] ) ? $this->data[ $key ] : $default_value;
295 }
296
297 /**
298 * @since 2.3.0
299 * @access protected
300 */
301 protected function set( $key, $value = null ) {
302 $this->init_data();
303
304 if ( is_array( $key ) ) {
305 $this->data = array_replace_recursive( $this->data, $key );
306 } else {
307 $this->data[ $key ] = $value;
308 }
309
310 $this->update_settings();
311 }
312
313 /**
314 * @since 2.3.0
315 * @access protected
316 */
317 protected function delete( $key = null ) {
318 $this->init_data();
319
320 if ( $key ) {
321 unset( $this->data[ $key ] );
322 } else {
323 $this->data = [];
324 }
325
326 $this->update_settings();
327 }
328
329 /**
330 * @since 2.3.0
331 * @access protected
332 */
333 protected function add( $key, $value, $default_value = '' ) {
334 $new_value = $this->get( $key, $default_value );
335
336 if ( is_array( $new_value ) ) {
337 $new_value[] = $value;
338 } elseif ( is_string( $new_value ) ) {
339 $new_value .= $value;
340 } elseif ( is_numeric( $new_value ) ) {
341 $new_value += $value;
342 }
343
344 $this->set( $key, $new_value );
345 }
346
347 /**
348 * @since 2.3.0
349 * @access protected
350 */
351 protected function add_notice( $content, $type = 'success' ) {
352 $this->add( 'notices', compact( 'content', 'type' ), [] );
353 }
354
355 /**
356 * @param $action
357 * @param array $request_body
358 * @param false $as_array
359 *
360 * @return mixed|\WP_Error
361 */
362 protected function request( $action, $request_body = [], $as_array = false ) {
363 $request_body = $this->get_connect_info() + $request_body;
364
365 return $this->http_request(
366 'POST',
367 $action,
368 [
369 'timeout' => 25,
370 'body' => $request_body,
371 'headers' => $this->is_connected() ?
372 [ 'X-Elementor-Signature' => $this->generate_signature( $request_body ) ] :
373 [],
374 ],
375 [
376 'return_type' => $as_array ? static::HTTP_RETURN_TYPE_ARRAY : static::HTTP_RETURN_TYPE_OBJECT,
377 ]
378 );
379 }
380
381 /**
382 * Get Base Connect Info
383 *
384 * Returns an array of connect info.
385 *
386 * @return array
387 */
388 protected function get_base_connect_info() {
389 return [
390 'app' => $this->get_slug(),
391 'access_token' => $this->get( 'access_token' ),
392 'client_id' => $this->get( 'client_id' ),
393 'local_id' => get_current_user_id(),
394 'site_key' => $this->get_site_key(),
395 'home_url' => trailingslashit( home_url() ),
396 ];
397 }
398
399 /**
400 * Get all the connect information
401 *
402 * @return array
403 */
404 protected function get_connect_info() {
405 $connect_info = $this->get_base_connect_info();
406
407 $additional_info = [];
408
409 /**
410 * Additional connect info.
411 *
412 * Filters the connection information when connecting to Elementor servers.
413 * This hook can be used to add more information or add more data.
414 *
415 * @param array $additional_info Additional connecting information array.
416 * @param Base_App $this The base app instance.
417 */
418 $additional_info = apply_filters( 'elementor/connect/additional-connect-info', $additional_info, $this );
419
420 return array_merge( $connect_info, $additional_info );
421 }
422
423 /**
424 * @param $endpoint
425 *
426 * @return array
427 */
428 protected function generate_authentication_headers( $endpoint ) {
429 $connect_info = ( new Collection( $this->get_connect_info() ) )
430 ->map_with_keys( function ( $value, $key ) {
431 // For bc `get_connect_info` returns the connect info with underscore,
432 // headers with underscore are not valid, so all the keys with underscore will be replaced to hyphen.
433 return [ str_replace( '_', '-', $key ) => $value ];
434 } )
435 ->replace_recursive( [ 'endpoint' => $endpoint ] )
436 ->sort_keys();
437
438 return $connect_info
439 ->merge( [ 'X-Elementor-Signature' => $this->generate_signature( $connect_info->all() ) ] )
440 ->all();
441 }
442
443 /**
444 * Send an http request
445 *
446 * @param $method
447 * @param $endpoint
448 * @param array $args
449 * @param array $options
450 *
451 * @return mixed|\WP_Error
452 */
453 protected function http_request( $method, $endpoint, $args = [], $options = [] ) {
454 $options = wp_parse_args( $options, [
455 'return_type' => static::HTTP_RETURN_TYPE_OBJECT,
456 ] );
457
458 $args = array_replace_recursive( [
459 'headers' => $this->is_connected() ? $this->generate_authentication_headers( $endpoint ) : [],
460 'method' => $method,
461 'timeout' => 10,
462 ], $args );
463
464 $response = $this->http->request_with_fallback(
465 $this->get_generated_urls( $endpoint ),
466 $args
467 );
468
469 if ( is_wp_error( $response ) && empty( $options['with_error_data'] ) ) {
470 // PHPCS - the variable $response does not contain a user input value.
471 wp_die( $response, [ 'back_link' => true ] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
472 }
473
474 $body = wp_remote_retrieve_body( $response );
475 $response_code = (int) wp_remote_retrieve_response_code( $response );
476
477 if ( ! $response_code ) {
478 return new \WP_Error( 500, 'No Response' );
479 }
480
481 // Server sent a success message without content.
482 if ( 'null' === $body ) {
483 $body = true;
484 }
485
486 $body = json_decode( $body, static::HTTP_RETURN_TYPE_ARRAY === $options['return_type'] );
487
488 if ( false === $body ) {
489 return new \WP_Error( 422, 'Wrong Server Response' );
490 }
491
492 if ( 201 === $response_code ) {
493 return $body;
494 }
495
496 if ( 200 !== $response_code ) {
497 // In case $as_array = true.
498 $body = (object) $body;
499
500 $message = isset( $body->message ) ? $body->message : wp_remote_retrieve_response_message( $response );
501 $code = (int) ( isset( $body->code ) ? $body->code : $response_code );
502
503 if ( ! $code ) {
504 $code = $response_code;
505 }
506
507 if ( 401 === $code ) {
508 $this->delete();
509
510 $should_retry = ! in_array( $this->auth_mode, [ 'xhr', 'cli' ], true );
511
512 if ( $should_retry ) {
513 $this->action_authorize();
514 }
515 }
516
517 if ( isset( $options['with_error_data'] ) && true === $options['with_error_data'] ) {
518 return new \WP_Error( $code, $message, $body );
519 }
520
521 return new \WP_Error( $code, $message );
522 }
523
524 return $body;
525 }
526
527 /**
528 * Create a signature for the http request
529 *
530 * @param array $payload
531 *
532 * @return false|string
533 */
534 protected function generate_signature( $payload = [] ) {
535 return hash_hmac(
536 'sha256',
537 wp_json_encode( $payload, JSON_NUMERIC_CHECK ),
538 $this->get( 'access_token_secret' )
539 );
540 }
541
542 /**
543 * @since 2.3.0
544 * @access protected
545 */
546 protected function get_api_url() {
547 return static::API_URL . '/' . $this->get_slug();
548 }
549 /**
550 * @since 2.3.0
551 * @access protected
552 */
553 protected function get_remote_site_url() {
554 return static::SITE_URL . '/' . $this->get_slug();
555 }
556
557 /**
558 * @since 2.3.0
559 * @access protected
560 */
561 protected function get_remote_authorize_url() {
562 $redirect_uri = $this->get_auth_redirect_uri();
563
564 $allowed_query_params_to_propagate = [
565 'utm_source',
566 'utm_medium',
567 'utm_campaign',
568 'utm_term',
569 'utm_content',
570 'source',
571 'screen_hint',
572 ];
573
574 $query_params = ( new Collection( $_GET ) ) // phpcs:ignore
575 ->only( $allowed_query_params_to_propagate )
576 ->merge( [
577 'action' => 'authorize',
578 'response_type' => 'code',
579 'client_id' => $this->get( 'client_id' ),
580 'auth_secret' => $this->get( 'auth_secret' ),
581 'state' => $this->get( 'state' ),
582 'redirect_uri' => rawurlencode( $redirect_uri ),
583 'may_share_data' => current_user_can( 'manage_options' ) && ! Tracker::is_allow_track(),
584 'reconnect_nonce' => wp_create_nonce( $this->get_slug() . 'reconnect' ),
585 ] );
586
587 $utm_campaign = get_transient( 'elementor_core_campaign' );
588
589 if ( ! empty( $utm_campaign ) ) {
590 foreach ( [ 'source', 'medium', 'campaign' ] as $key ) {
591 if ( ! empty( $utm_campaign[ $key ] ) ) {
592 $query_params->offsetSet( 'utm_' . $key, $utm_campaign[ $key ] );
593 }
594 }
595 }
596
597 return add_query_arg( $query_params->all(), $this->get_remote_site_url() );
598 }
599
600 /**
601 * @since 2.3.0
602 * @access protected
603 */
604 protected function redirect_to_admin_page( $url = '' ) {
605 if ( ! $url ) {
606 $url = Admin::$url;
607 }
608
609 switch ( $this->auth_mode ) {
610 case 'popup':
611 $this->print_popup_close_script( $url );
612 break;
613
614 case 'cli':
615 case 'rest':
616 $this->admin_notice();
617 die;
618
619 default:
620 wp_safe_redirect( $url );
621 die;
622 }
623 }
624
625 /**
626 * @since 2.3.0
627 * @access protected
628 */
629 protected function set_client_id() {
630 $source = Utils::get_super_global_value( $_REQUEST, 'source' ) ?? ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
631 $response = $this->request(
632 'get_client_id',
633 [
634 'source' => esc_attr( $source ),
635 ]
636 );
637
638 if ( is_wp_error( $response ) ) {
639 // PHPCS - the variable $response does not contain a user input value.
640 wp_die( $response, $response->get_error_message() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
641 }
642
643 $this->set( 'client_id', $response->client_id );
644 $this->set( 'auth_secret', $response->auth_secret );
645 }
646
647 /**
648 * @since 2.3.0
649 * @access protected
650 */
651 protected function set_request_state() {
652 $this->set( 'state', wp_generate_password( 12, false ) );
653 }
654
655 protected function get_popup_success_event_data() {
656 return [];
657 }
658
659 /**
660 * @since 2.3.0
661 * @access protected
662 */
663 protected function print_popup_close_script( $url ) {
664 $data = $this->get_popup_success_event_data();
665
666 ?>
667 <script>
668 if ( opener && opener !== window ) {
669 opener.jQuery( 'body' ).trigger(
670 'elementor/connect/success/<?php echo esc_attr( Utils::get_super_global_value( $_REQUEST, 'callback_id' ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here. ?>',
671 <?php echo wp_json_encode( $data ); ?>
672 );
673
674 opener.dispatchEvent( new CustomEvent( 'elementor/connect/success', { detail: <?php echo wp_json_encode( $data ); ?> } ) );
675
676 window.close();
677 opener.focus();
678 } else {
679 location = '<?php echo esc_url( $url ); ?>';
680 }
681 </script>
682 <?php
683 die;
684 }
685
686 /**
687 * @since 2.3.0
688 * @access protected
689 */
690 protected function disconnect() {
691 if ( $this->is_connected() ) {
692 // Try update the server, but not needed to handle errors.
693 $this->request( 'disconnect' );
694 }
695
696 $this->delete();
697 }
698
699 /**
700 * @since 2.3.0
701 * @access protected
702 */
703 public function get_site_key() {
704 $site_key = get_option( static::OPTION_CONNECT_SITE_KEY );
705
706 if ( ! $site_key ) {
707 $site_key = md5( uniqid( wp_generate_password() ) );
708 update_option( static::OPTION_CONNECT_SITE_KEY, $site_key );
709 }
710
711 return $site_key;
712 }
713
714 protected function redirect_to_remote_authorize_url() {
715 switch ( $this->auth_mode ) {
716 case 'cli':
717 case 'rest':
718 $this->get_app_token_from_cli_token( Utils::get_super_global_value( $_REQUEST, 'token' ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
719 return;
720 default:
721 wp_redirect( $this->get_remote_authorize_url() ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- Safe redirect is used here.
722 die;
723 }
724 }
725
726 protected function get_auth_redirect_uri() {
727 $redirect_uri = $this->get_admin_url( 'get_token' );
728
729 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
730 $val = Utils::get_super_global_value( $_REQUEST, 'redirect_to' );
731 if ( $val ) {
732 $redirect_uri = add_query_arg( [ 'redirect_to' => $val ], $redirect_uri );
733 }
734
735 switch ( $this->auth_mode ) {
736 case 'popup':
737 $redirect_uri = add_query_arg( [
738 'mode' => 'popup',
739 'callback_id' => esc_attr( Utils::get_super_global_value( $_REQUEST, 'callback_id' ) ), //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here.
740 ], $redirect_uri );
741 break;
742 }
743
744 return $redirect_uri;
745 }
746
747
748 protected function print_notices( $notices ) {
749 switch ( $this->auth_mode ) {
750 case 'cli':
751 foreach ( $notices as $notice ) {
752 printf( '[%s] %s', wp_kses_post( $notice['type'] ), wp_kses_post( $notice['content'] ) );
753 }
754 break;
755
756 case 'rest':
757 // After `wp_send_json` the script will die.
758 $this->delete( 'notices' );
759 wp_send_json( $notices );
760 break;
761
762 default:
763 /**
764 * @var Admin_Notices $admin_notices
765 */
766 $admin_notices = Plugin::$instance->admin->get_component( 'admin-notices' );
767
768 foreach ( $notices as $notice ) {
769 $options = [
770 'description' => wp_kses_post( wpautop( $notice['content'] ) ),
771 'type' => $notice['type'],
772 'icon' => false,
773 ];
774
775 $admin_notices->print_admin_notice( $options );
776 }
777 }
778 }
779
780 protected function get_app_info() {
781 return [];
782 }
783
784 protected function print_app_info() {
785 $app_info = $this->get_app_info();
786
787 foreach ( $app_info as $key => $item ) {
788 if ( $item['value'] ) {
789 $status = 'Exist';
790 $color = 'green';
791 } else {
792 $status = 'Empty';
793 $color = 'red';
794 }
795
796 // PHPCS - the values of $item['label'], $color, $status are plain strings.
797 printf( '%s: <strong style="color:%s">%s</strong><br>', $item['label'], $color, $status ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
798 }
799 }
800
801 protected function get_generated_urls( $endpoint ) {
802 $base_urls = $this->get_api_url();
803
804 if ( ! is_array( $base_urls ) ) {
805 $base_urls = [ $base_urls ];
806 }
807
808 return array_map( function ( $base_url ) use ( $endpoint ) {
809 return trailingslashit( $base_url ) . $endpoint;
810 }, $base_urls );
811 }
812
813 private function init_auth_mode() {
814 $is_rest = defined( 'REST_REQUEST' ) && REST_REQUEST;
815 $is_ajax = wp_doing_ajax();
816
817 if ( $is_rest || $is_ajax ) {
818 // Set default to 'xhr' if rest or ajax request.
819 $this->set_auth_mode( 'xhr' );
820 }
821
822 $mode = Utils::get_super_global_value( $_REQUEST, 'mode' );
823
824 if ( $mode ) {
825 $allowed_auth_modes = [
826 'popup',
827 ];
828
829 if ( defined( 'WP_CLI' ) && WP_CLI ) {
830 $allowed_auth_modes[] = 'cli';
831 }
832
833 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
834 $allowed_auth_modes[] = 'rest';
835 }
836
837 if ( in_array( $mode, $allowed_auth_modes, true ) ) {
838 $this->set_auth_mode( $mode );
839 }
840 }
841 }
842
843 public function set_auth_mode( $mode ) {
844 $this->auth_mode = $mode;
845 }
846
847 /**
848 * @since 2.3.0
849 * @access public
850 */
851 public function __construct() {
852 add_action( 'admin_notices', [ $this, 'admin_notice' ] );
853
854 $this->init_auth_mode();
855
856 $this->http = new Http();
857
858 /**
859 * Allow extended apps to customize the __construct without call parent::__construct.
860 */
861 $this->init();
862 }
863 }
864