Version Description
- 2020-09-02 =
- Add - Initial support for WooCommerce Subscriptions: Signing up for subscriptions, scheduled payments, and customer-initiated payment method changes.
- Add - Added a link to transaction details from order screens.
- Add - Allow merchant to edit statement descriptor.
- Fix - Do not redirect to the onboarding page after completing the WC4.5-beta wizard.
- Fix - Save order metadata before the payment is completed to avoid missing payments.
- Update - Bumped the minimum Jetpack requirement to version 8.2.
Download this release
Release Info
Developer | automattic |
Plugin | WooCommerce Payments – Fully Integrated Solution Built and Supported by Woo |
Version | 1.4.0 |
Comparing to | |
See all releases |
Code changes from version 1.3.0 to 1.4.0
- changelog.txt +8 -0
- includes/class-wc-payment-gateway-wcpay.php +399 -196
- includes/class-wc-payments-account.php +57 -1
- includes/class-wc-payments-token-service.php +6 -4
- includes/class-wc-payments.php +9 -2
- includes/compat/subscriptions/class-wc-payment-gateway-wcpay-subscriptions-compat.php +139 -0
- includes/data-types/class-payment-information.php +176 -0
- includes/wc-payment-api/class-wc-payments-api-client.php +25 -2
- includes/wc-payment-api/class-wc-payments-http.php +7 -4
- readme.txt +10 -2
- vendor/autoload.php +1 -1
- vendor/autoload_packages.php +2 -2
- vendor/automattic/jetpack-config/src/class-config.php +17 -7
- vendor/automattic/jetpack-connection/.gitignore +1 -0
- vendor/automattic/jetpack-connection/composer.json +8 -5
- vendor/automattic/jetpack-connection/docs/register-site.md +92 -5
- vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php +3 -2
- vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php +6 -3
- vendor/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php +25 -19
- vendor/automattic/jetpack-connection/src/class-error-handler.php +621 -0
- vendor/automattic/jetpack-connection/src/class-manager.php +237 -49
- vendor/automattic/jetpack-connection/src/class-plugin-storage.php +55 -18
- vendor/automattic/jetpack-connection/src/class-plugin.php +28 -1
- vendor/automattic/jetpack-connection/src/class-rest-connector.php +234 -6
- vendor/automattic/jetpack-connection/src/error-handlers/class-invalid-blog-token.php +94 -0
- vendor/automattic/jetpack-constants/phpunit.xml +7 -0
- vendor/automattic/jetpack-options/composer.json +1 -1
- vendor/automattic/jetpack-options/legacy/class-jetpack-options.php +26 -3
- vendor/automattic/jetpack-roles/phpunit.xml +7 -0
- vendor/automattic/jetpack-status/README.md +34 -0
- vendor/automattic/jetpack-status/composer.json +31 -0
- vendor/automattic/jetpack-status/src/class-status.php +235 -0
- vendor/composer/ClassLoader.php +1 -1
- vendor/composer/autoload_classmap.php +3 -0
- vendor/composer/autoload_classmap_package.php +28 -16
- vendor/composer/autoload_files_package.php +1 -1
- vendor/composer/autoload_real.php +7 -10
- vendor/composer/autoload_static.php +7 -4
- vendor/composer/installed.json +70 -34
- woocommerce-payments.php +35 -2
changelog.txt
CHANGED
@@ -1,5 +1,13 @@
|
|
1 |
*** WooCommerce Payments Changelog ***
|
2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
= 1.3.0 - 2020-08-17 =
|
4 |
* Add - Support for saved cards.
|
5 |
* Add - Search bar for transactions.
|
1 |
*** WooCommerce Payments Changelog ***
|
2 |
|
3 |
+
= 1.4.0 - 2020-09-02 =
|
4 |
+
* Add - Initial support for WooCommerce Subscriptions: Signing up for subscriptions, scheduled payments, and customer-initiated payment method changes.
|
5 |
+
* Add - Added a link to transaction details from order screens.
|
6 |
+
* Add - Allow merchant to edit statement descriptor.
|
7 |
+
* Fix - Do not redirect to the onboarding page after completing the WC4.5-beta wizard.
|
8 |
+
* Fix - Save order metadata before the payment is completed to avoid missing payments.
|
9 |
+
* Update - Bumped the minimum Jetpack requirement to version 8.2.
|
10 |
+
|
11 |
= 1.3.0 - 2020-08-17 =
|
12 |
* Add - Support for saved cards.
|
13 |
* Add - Search bar for transactions.
|
includes/class-wc-payment-gateway-wcpay.php
CHANGED
@@ -10,6 +10,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|
10 |
}
|
11 |
|
12 |
use WCPay\Logger;
|
|
|
13 |
use WCPay\Exceptions\WC_Payments_Intent_Authentication_Exception;
|
14 |
use WCPay\Tracker;
|
15 |
|
@@ -99,27 +100,32 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
99 |
|
100 |
// Define setting fields.
|
101 |
$this->form_fields = [
|
102 |
-
'enabled'
|
103 |
'title' => __( 'Enable/disable', 'woocommerce-payments' ),
|
104 |
'label' => __( 'Enable WooCommerce Payments', 'woocommerce-payments' ),
|
105 |
'type' => 'checkbox',
|
106 |
'description' => '',
|
107 |
'default' => 'no',
|
108 |
],
|
109 |
-
'account_details'
|
110 |
'type' => 'account_actions',
|
111 |
],
|
112 |
-
'account_status'
|
113 |
'type' => 'account_status',
|
114 |
],
|
115 |
-
'
|
|
|
|
|
|
|
|
|
|
|
116 |
'title' => __( 'Manual capture', 'woocommerce-payments' ),
|
117 |
'label' => __( 'Issue an authorization on checkout, and capture later.', 'woocommerce-payments' ),
|
118 |
'type' => 'checkbox',
|
119 |
'description' => __( 'Charge must be captured within 7 days of authorization, otherwise the authorization and order will be canceled.', 'woocommerce-payments' ),
|
120 |
'default' => 'no',
|
121 |
],
|
122 |
-
'test_mode'
|
123 |
'title' => __( 'Test mode', 'woocommerce-payments' ),
|
124 |
'label' => __( 'Enable test mode', 'woocommerce-payments' ),
|
125 |
'type' => 'checkbox',
|
@@ -127,7 +133,7 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
127 |
'default' => 'no',
|
128 |
'desc_tip' => true,
|
129 |
],
|
130 |
-
'enable_logging'
|
131 |
'title' => __( 'Debug log', 'woocommerce-payments' ),
|
132 |
'label' => __( 'When enabled debug notes will be added to the log.', 'woocommerce-payments' ),
|
133 |
'type' => 'checkbox',
|
@@ -146,7 +152,9 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
146 |
// Load the settings.
|
147 |
$this->init_settings();
|
148 |
|
|
|
149 |
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] );
|
|
|
150 |
add_action( 'woocommerce_order_actions', [ $this, 'add_order_actions' ] );
|
151 |
add_action( 'woocommerce_order_action_capture_charge', [ $this, 'capture_charge' ] );
|
152 |
add_action( 'woocommerce_order_action_cancel_authorization', [ $this, 'cancel_authorization' ] );
|
@@ -388,223 +396,263 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
388 |
/**
|
389 |
* Process the payment for a given order.
|
390 |
*
|
391 |
-
* @param int
|
|
|
392 |
*
|
393 |
* @return array|null An array with result of payment and redirect URL, or nothing.
|
394 |
*/
|
395 |
-
public function process_payment( $order_id ) {
|
396 |
$order = wc_get_order( $order_id );
|
397 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
398 |
}
|
399 |
|
400 |
/**
|
401 |
* Process the payment for a given order.
|
402 |
*
|
403 |
-
* @param WC_Order
|
404 |
-
* @param WC_Cart
|
|
|
|
|
|
|
405 |
*
|
406 |
* @return array|null An array with result of payment and redirect URL, or nothing.
|
|
|
407 |
*/
|
408 |
-
public function process_payment_for_order( $order, $cart ) {
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
$user = wp_get_current_user();
|
424 |
-
$customer_id = $this->customer_service->get_customer_id_by_user_id( $user->ID );
|
425 |
-
|
426 |
-
if ( null === $customer_id ) {
|
427 |
-
// Create a new customer.
|
428 |
-
$customer_id = $this->customer_service->create_customer_for_user( $user, $name, $email );
|
429 |
-
} else {
|
430 |
-
// Update the existing customer with the current details. In the event the old customer can't be
|
431 |
-
// found a new one is created, so we update the customer ID here as well.
|
432 |
-
$customer_id = $this->customer_service->update_customer_for_user( $customer_id, $user, $name, $email );
|
433 |
-
}
|
434 |
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
// If updating the payment method fails, log the error message but catch the error to avoid crashing the checkout flow.
|
441 |
-
Logger::log( 'Error when updating saved payment method: ' . $e->getMessage() );
|
442 |
-
}
|
443 |
-
}
|
444 |
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
$payment_method,
|
457 |
-
$customer_id,
|
458 |
-
$manual_capture,
|
459 |
-
$save_payment_method,
|
460 |
-
$metadata,
|
461 |
-
$this->get_level3_data_from_order( $order )
|
462 |
-
);
|
463 |
|
464 |
-
|
465 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
466 |
|
467 |
-
|
468 |
-
try {
|
469 |
-
$this->token_service->add_payment_method_to_user( $payment_method, wp_get_current_user() );
|
470 |
-
} catch ( Exception $e ) {
|
471 |
-
// If saving the token fails, log the error message but catch the error to avoid crashing the checkout flow.
|
472 |
-
Logger::log( 'Error when saving payment method: ' . $e->getMessage() );
|
473 |
-
}
|
474 |
-
}
|
475 |
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
/* translators: %1: the successfully charged amount, %2: transaction ID of the payment */
|
481 |
-
__( 'A payment of %1$s was <strong>successfully charged</strong> using WooCommerce Payments (<code>%2$s</code>).', 'woocommerce-payments' ),
|
482 |
-
[
|
483 |
-
'strong' => '<strong>',
|
484 |
-
'code' => '<code>',
|
485 |
-
]
|
486 |
-
),
|
487 |
-
wc_price( $amount ),
|
488 |
-
$intent_id
|
489 |
-
);
|
490 |
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
495 |
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
'code' => '<code>',
|
507 |
-
]
|
508 |
-
),
|
509 |
-
wc_price( $amount ),
|
510 |
-
$intent_id
|
511 |
-
);
|
512 |
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
// Add a note in case the customer does not complete the payment (exits the page),
|
524 |
-
// so the store owner has some information about what happened to create an order.
|
525 |
-
$note = sprintf(
|
526 |
-
WC_Payments_Utils::esc_interpolated_html(
|
527 |
-
/* translators: %1: the authorized amount, %2: transaction ID of the payment */
|
528 |
-
__( 'A payment of %1$s was <strong>started</strong> using WooCommerce Payments (<code>%2$s</code>).', 'woocommerce-payments' ),
|
529 |
-
[
|
530 |
-
'strong' => '<strong>',
|
531 |
-
'code' => '<code>',
|
532 |
-
]
|
533 |
-
),
|
534 |
-
wc_price( $amount ),
|
535 |
-
$intent_id
|
536 |
-
);
|
537 |
-
$order->add_order_note( $note );
|
538 |
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
wp_create_nonce( 'wcpay_update_order_status_nonce' )
|
552 |
-
),
|
553 |
-
];
|
554 |
}
|
555 |
-
} else {
|
556 |
-
$order->payment_complete();
|
557 |
}
|
558 |
|
559 |
-
|
560 |
-
|
|
|
|
|
561 |
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
569 |
|
570 |
-
|
|
|
|
|
|
|
571 |
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
578 |
|
579 |
-
|
580 |
-
|
581 |
-
*
|
582 |
-
* @return string
|
583 |
-
* @throws Exception - If no payment method is found.
|
584 |
-
*/
|
585 |
-
private function get_payment_method_from_request() {
|
586 |
-
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
587 |
-
if ( empty( $_POST['wcpay-payment-method'] ) && empty( $_POST[ 'wc-' . self::GATEWAY_ID . '-payment-token' ] ) ) {
|
588 |
-
// If no payment method is set then stop here with an error.
|
589 |
-
throw new Exception( __( 'Payment method not found.', 'woocommerce-payments' ) );
|
590 |
-
}
|
591 |
|
592 |
-
|
|
|
|
|
|
|
593 |
|
594 |
-
|
595 |
-
|
596 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
597 |
|
598 |
-
|
599 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
600 |
}
|
|
|
601 |
|
602 |
-
|
|
|
|
|
603 |
}
|
604 |
|
605 |
-
|
|
|
|
|
|
|
|
|
606 |
|
607 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
608 |
}
|
609 |
|
610 |
/**
|
@@ -717,6 +765,127 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
717 |
return ob_get_clean();
|
718 |
}
|
719 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
720 |
/**
|
721 |
* Generate markup for account actions
|
722 |
*/
|
@@ -995,6 +1164,12 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
995 |
$intent_id
|
996 |
);
|
997 |
$order->add_order_note( $note );
|
|
|
|
|
|
|
|
|
|
|
|
|
998 |
$order->payment_complete( $intent_id );
|
999 |
break;
|
1000 |
case 'requires_capture':
|
@@ -1013,6 +1188,12 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
1013 |
// Save the note separately because if there is no change in status
|
1014 |
// then the note is not saved using WC_Order::update_status.
|
1015 |
$order->add_order_note( $note );
|
|
|
|
|
|
|
|
|
|
|
|
|
1016 |
$order->update_status( 'on-hold' );
|
1017 |
$order->set_transaction_id( $intent_id );
|
1018 |
break;
|
@@ -1037,16 +1218,12 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
1037 |
}
|
1038 |
|
1039 |
if ( 'succeeded' === $status || 'requires_capture' === $status ) {
|
1040 |
-
// The order is successful, so update it to reflect that.
|
1041 |
-
$order->update_meta_data( '_charge_id', $intent->get_charge_id() );
|
1042 |
-
$order->update_meta_data( '_intention_status', $status );
|
1043 |
-
$order->save();
|
1044 |
-
|
1045 |
wc_reduce_stock_levels( $order_id );
|
1046 |
WC()->cart->empty_cart();
|
1047 |
|
1048 |
if ( ! empty( $payment_method_id ) ) {
|
1049 |
try {
|
|
|
1050 |
$this->token_service->add_payment_method_to_user( $payment_method_id, wp_get_current_user() );
|
1051 |
} catch ( Exception $e ) {
|
1052 |
// If saving the token fails, log the error message but catch the error to avoid crashing the checkout flow.
|
@@ -1112,13 +1289,13 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
1112 |
public function add_payment_method() {
|
1113 |
try {
|
1114 |
|
1115 |
-
// phpcs:
|
1116 |
if ( ! isset( $_POST['wcpay-setup-intent'] ) ) {
|
1117 |
throw new Exception( __( 'A WooCommerce Payments payment method was not provided', 'woocommerce-payments' ) );
|
1118 |
}
|
1119 |
|
|
|
1120 |
$setup_intent_id = ! empty( $_POST['wcpay-setup-intent'] ) ? wc_clean( $_POST['wcpay-setup-intent'] ) : false;
|
1121 |
-
// phpcs:enable WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
|
1122 |
|
1123 |
$customer_id = $this->customer_service->get_customer_id_by_user_id( get_current_user_id() );
|
1124 |
|
@@ -1154,7 +1331,8 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
1154 |
* @throws Exception - When an error occurs in setup intent creation.
|
1155 |
*/
|
1156 |
public function create_setup_intent() {
|
1157 |
-
|
|
|
1158 |
|
1159 |
// Determine the customer adding the payment method, create one if we don't have one already.
|
1160 |
$user = wp_get_current_user();
|
@@ -1164,7 +1342,7 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
1164 |
}
|
1165 |
|
1166 |
return $this->payments_api_client->create_setup_intent(
|
1167 |
-
$
|
1168 |
$customer_id
|
1169 |
);
|
1170 |
}
|
@@ -1195,4 +1373,29 @@ class WC_Payment_Gateway_WCPay extends WC_Payment_Gateway_CC {
|
|
1195 |
);
|
1196 |
}
|
1197 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1198 |
}
|
10 |
}
|
11 |
|
12 |
use WCPay\Logger;
|
13 |
+
use WCPay\DataTypes\Payment_Information;
|
14 |
use WCPay\Exceptions\WC_Payments_Intent_Authentication_Exception;
|
15 |
use WCPay\Tracker;
|
16 |
|
100 |
|
101 |
// Define setting fields.
|
102 |
$this->form_fields = [
|
103 |
+
'enabled' => [
|
104 |
'title' => __( 'Enable/disable', 'woocommerce-payments' ),
|
105 |
'label' => __( 'Enable WooCommerce Payments', 'woocommerce-payments' ),
|
106 |
'type' => 'checkbox',
|
107 |
'description' => '',
|
108 |
'default' => 'no',
|
109 |
],
|
110 |
+
'account_details' => [
|
111 |
'type' => 'account_actions',
|
112 |
],
|
113 |
+
'account_status' => [
|
114 |
'type' => 'account_status',
|
115 |
],
|
116 |
+
'account_statement_descriptor' => [
|
117 |
+
'type' => 'account_statement_descriptor',
|
118 |
+
'title' => __( 'Customer bank statement', 'woocommerce-payments' ),
|
119 |
+
'description' => __( 'Edit the way your store name appears on your customers’ bank statements.', 'woocommerce-payments' ),
|
120 |
+
],
|
121 |
+
'manual_capture' => [
|
122 |
'title' => __( 'Manual capture', 'woocommerce-payments' ),
|
123 |
'label' => __( 'Issue an authorization on checkout, and capture later.', 'woocommerce-payments' ),
|
124 |
'type' => 'checkbox',
|
125 |
'description' => __( 'Charge must be captured within 7 days of authorization, otherwise the authorization and order will be canceled.', 'woocommerce-payments' ),
|
126 |
'default' => 'no',
|
127 |
],
|
128 |
+
'test_mode' => [
|
129 |
'title' => __( 'Test mode', 'woocommerce-payments' ),
|
130 |
'label' => __( 'Enable test mode', 'woocommerce-payments' ),
|
131 |
'type' => 'checkbox',
|
133 |
'default' => 'no',
|
134 |
'desc_tip' => true,
|
135 |
],
|
136 |
+
'enable_logging' => [
|
137 |
'title' => __( 'Debug log', 'woocommerce-payments' ),
|
138 |
'label' => __( 'When enabled debug notes will be added to the log.', 'woocommerce-payments' ),
|
139 |
'type' => 'checkbox',
|
152 |
// Load the settings.
|
153 |
$this->init_settings();
|
154 |
|
155 |
+
add_filter( 'woocommerce_settings_api_sanitized_fields_' . $this->id, [ $this, 'sanitize_plugin_settings' ] );
|
156 |
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] );
|
157 |
+
add_action( 'admin_notices', [ $this, 'display_errors' ], 9999 );
|
158 |
add_action( 'woocommerce_order_actions', [ $this, 'add_order_actions' ] );
|
159 |
add_action( 'woocommerce_order_action_capture_charge', [ $this, 'capture_charge' ] );
|
160 |
add_action( 'woocommerce_order_action_cancel_authorization', [ $this, 'cancel_authorization' ] );
|
396 |
/**
|
397 |
* Process the payment for a given order.
|
398 |
*
|
399 |
+
* @param int $order_id Order ID to process the payment for.
|
400 |
+
* @param bool $force_save_payment_method Whether this is a one-off payment (false) or it's the first installment of a recurring payment (true).
|
401 |
*
|
402 |
* @return array|null An array with result of payment and redirect URL, or nothing.
|
403 |
*/
|
404 |
+
public function process_payment( $order_id, $force_save_payment_method = false ) {
|
405 |
$order = wc_get_order( $order_id );
|
406 |
+
|
407 |
+
try {
|
408 |
+
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
409 |
+
$payment_information = Payment_Information::from_payment_request( $_POST );
|
410 |
+
$manual_capture = 'yes' === $this->get_option( 'manual_capture' );
|
411 |
+
|
412 |
+
return $this->process_payment_for_order( $order, WC()->cart, $payment_information, $manual_capture, $force_save_payment_method );
|
413 |
+
} catch ( Exception $e ) {
|
414 |
+
// TODO: Create plugin specific exceptions so that we can be smarter about what we create notices for.
|
415 |
+
wc_add_notice( $e->getMessage(), 'error' );
|
416 |
+
|
417 |
+
$order->update_status( 'failed' );
|
418 |
+
|
419 |
+
return [
|
420 |
+
'result' => 'fail',
|
421 |
+
'redirect' => '',
|
422 |
+
];
|
423 |
+
}
|
424 |
}
|
425 |
|
426 |
/**
|
427 |
* Process the payment for a given order.
|
428 |
*
|
429 |
+
* @param WC_Order $order Order.
|
430 |
+
* @param WC_Cart $cart Cart.
|
431 |
+
* @param WC_Payment_Information $payment_information Payment info.
|
432 |
+
* @param bool $manual_capture Indicates whether this payment is merchant-initiated (true) or customer-initated (false).
|
433 |
+
* @param bool $force_save_payment_method Whether this is a one-off payment (false) or it's the first installment of a recurring payment (true).
|
434 |
*
|
435 |
* @return array|null An array with result of payment and redirect URL, or nothing.
|
436 |
+
* @throws WC_Payments_API_Exception Error processing the payment.
|
437 |
*/
|
438 |
+
public function process_payment_for_order( $order, $cart, $payment_information, $manual_capture, $force_save_payment_method = false ) {
|
439 |
+
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
440 |
+
$save_payment_method = ! $payment_information->is_using_saved_payment_method() && ( ! empty( $_POST[ 'wc-' . self::GATEWAY_ID . '-new-payment-method' ] ) || $force_save_payment_method );
|
441 |
+
|
442 |
+
$order_id = $order->get_id();
|
443 |
+
$amount = $order->get_total();
|
444 |
+
$user = $order->get_user() ?? wp_get_current_user();
|
445 |
+
$name = sanitize_text_field( $order->get_billing_first_name() ) . ' ' . sanitize_text_field( $order->get_billing_last_name() );
|
446 |
+
$email = sanitize_email( $order->get_billing_email() );
|
447 |
+
$metadata = [
|
448 |
+
'customer_name' => $name,
|
449 |
+
'customer_email' => $email,
|
450 |
+
'site_url' => esc_url( get_site_url() ),
|
451 |
+
'order_id' => $order_id,
|
452 |
+
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
453 |
|
454 |
+
// We only force save the card during subscriptions.
|
455 |
+
// TODO: This is a bit flawed; make these 2 functionalities distinct.
|
456 |
+
if ( $force_save_payment_method ) {
|
457 |
+
$metadata['payment_type'] = 'recurring';
|
458 |
+
}
|
|
|
|
|
|
|
|
|
459 |
|
460 |
+
// Determine the customer making the payment, create one if we don't have one already.
|
461 |
+
$customer_id = $this->customer_service->get_customer_id_by_user_id( $user->ID );
|
462 |
+
|
463 |
+
if ( null === $customer_id ) {
|
464 |
+
// Create a new customer.
|
465 |
+
$customer_id = $this->customer_service->create_customer_for_user( $user, $name, $email );
|
466 |
+
} else {
|
467 |
+
// Update the existing customer with the current details. In the event the old customer can't be
|
468 |
+
// found a new one is created, so we update the customer ID here as well.
|
469 |
+
$customer_id = $this->customer_service->update_customer_for_user( $customer_id, $user, $name, $email );
|
470 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
471 |
|
472 |
+
// Update saved payment method information with checkout values, as some saved methods might not have billing details.
|
473 |
+
if ( $payment_information->is_using_saved_payment_method() ) {
|
474 |
+
try {
|
475 |
+
$this->customer_service->update_payment_method_with_billing_details_from_order( $payment_information->get_payment_method(), $order );
|
476 |
+
} catch ( Exception $e ) {
|
477 |
+
// If updating the payment method fails, log the error message but catch the error to avoid crashing the checkout flow.
|
478 |
+
Logger::log( 'Error when updating saved payment method: ' . $e->getMessage() );
|
479 |
+
}
|
480 |
+
}
|
481 |
|
482 |
+
$intent_failed = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
483 |
|
484 |
+
// In case amount is 0 and we're not saving the payment method, we won't be using intents and can confirm the order payment.
|
485 |
+
if ( 0 === $amount && ! $save_payment_method ) {
|
486 |
+
$order->payment_complete();
|
487 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
488 |
|
489 |
+
if ( $amount > 0 ) {
|
490 |
+
// Create intention, try to confirm it & capture the charge (if 3DS is not required).
|
491 |
+
$intent = $this->payments_api_client->create_and_confirm_intention(
|
492 |
+
WC_Payments_Utils::prepare_amount( $amount, 'USD' ),
|
493 |
+
'usd',
|
494 |
+
$payment_information->get_payment_method(),
|
495 |
+
$customer_id,
|
496 |
+
$manual_capture,
|
497 |
+
$save_payment_method,
|
498 |
+
$metadata,
|
499 |
+
$this->get_level3_data_from_order( $order ),
|
500 |
+
$payment_information->is_merchant_initiated()
|
501 |
+
);
|
502 |
|
503 |
+
$intent_id = $intent->get_id();
|
504 |
+
$status = $intent->get_status();
|
505 |
+
$charge_id = $intent->get_charge_id();
|
506 |
+
} else {
|
507 |
+
// For $0 orders, we need to save the payment method using a setup intent.
|
508 |
+
$intent = $this->payments_api_client->create_setup_intent(
|
509 |
+
$payment_information->get_payment_method(),
|
510 |
+
$customer_id,
|
511 |
+
'true'
|
512 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
513 |
|
514 |
+
$intent_id = $intent['id'];
|
515 |
+
$status = $intent['status'];
|
516 |
+
$charge_id = '';
|
517 |
+
|
518 |
+
// In SCA cases the setup intent status might be requires_action and we should display the authentication modal.
|
519 |
+
// For now, since we're not supporting SCA cards, we can ignore that status.
|
520 |
+
if ( 'succeeded' !== $status ) {
|
521 |
+
throw new Exception( __( 'Failed to add the provided payment method. Please try again later', 'woocommerce-payments' ) );
|
522 |
+
}
|
523 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
524 |
|
525 |
+
if ( ! empty( $intent ) ) {
|
526 |
+
if ( 'succeeded' !== $status && 'requires_capture' !== $status ) {
|
527 |
+
$intent_failed = true;
|
528 |
+
}
|
529 |
+
|
530 |
+
if ( $save_payment_method && ! $intent_failed ) {
|
531 |
+
try {
|
532 |
+
$token = $this->token_service->add_payment_method_to_user( $payment_information->get_payment_method(), $user );
|
533 |
+
$payment_information->set_token( $token );
|
534 |
+
} catch ( Exception $e ) {
|
535 |
+
// If saving the token fails, log the error message but catch the error to avoid crashing the checkout flow.
|
536 |
+
Logger::log( 'Error when saving payment method: ' . $e->getMessage() );
|
|
|
|
|
|
|
537 |
}
|
|
|
|
|
538 |
}
|
539 |
|
540 |
+
if ( $payment_information->is_using_saved_payment_method() ) {
|
541 |
+
$token = $payment_information->get_payment_token();
|
542 |
+
$this->add_token_to_order( $order, $token );
|
543 |
+
}
|
544 |
|
545 |
+
switch ( $status ) {
|
546 |
+
case 'succeeded':
|
547 |
+
$note = sprintf(
|
548 |
+
WC_Payments_Utils::esc_interpolated_html(
|
549 |
+
/* translators: %1: the successfully charged amount, %2: transaction ID of the payment */
|
550 |
+
__( 'A payment of %1$s was <strong>successfully charged</strong> using WooCommerce Payments (<code>%2$s</code>).', 'woocommerce-payments' ),
|
551 |
+
[
|
552 |
+
'strong' => '<strong>',
|
553 |
+
'code' => '<code>',
|
554 |
+
]
|
555 |
+
),
|
556 |
+
wc_price( $amount ),
|
557 |
+
$intent_id
|
558 |
+
);
|
559 |
|
560 |
+
$order->update_meta_data( '_intent_id', $intent_id );
|
561 |
+
$order->update_meta_data( '_charge_id', $charge_id );
|
562 |
+
$order->update_meta_data( '_intention_status', $status );
|
563 |
+
$order->save();
|
564 |
|
565 |
+
if ( $amount > 0 ) {
|
566 |
+
$order->add_order_note( $note );
|
567 |
+
}
|
568 |
+
$order->payment_complete( $intent_id );
|
569 |
+
break;
|
570 |
+
case 'requires_capture':
|
571 |
+
$note = sprintf(
|
572 |
+
WC_Payments_Utils::esc_interpolated_html(
|
573 |
+
/* translators: %1: the authorized amount, %2: transaction ID of the payment */
|
574 |
+
__( 'A payment of %1$s was <strong>authorized</strong> using WooCommerce Payments (<code>%2$s</code>).', 'woocommerce-payments' ),
|
575 |
+
[
|
576 |
+
'strong' => '<strong>',
|
577 |
+
'code' => '<code>',
|
578 |
+
]
|
579 |
+
),
|
580 |
+
wc_price( $amount ),
|
581 |
+
$intent_id
|
582 |
+
);
|
583 |
|
584 |
+
$order->update_status( 'on-hold', $note );
|
585 |
+
$order->set_transaction_id( $intent_id );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
586 |
|
587 |
+
$order->update_meta_data( '_intent_id', $intent_id );
|
588 |
+
$order->update_meta_data( '_charge_id', $charge_id );
|
589 |
+
$order->update_meta_data( '_intention_status', $status );
|
590 |
+
$order->save();
|
591 |
|
592 |
+
break;
|
593 |
+
case 'requires_action':
|
594 |
+
// Add a note in case the customer does not complete the payment (exits the page),
|
595 |
+
// so the store owner has some information about what happened to create an order.
|
596 |
+
$note = sprintf(
|
597 |
+
WC_Payments_Utils::esc_interpolated_html(
|
598 |
+
/* translators: %1: the authorized amount, %2: transaction ID of the payment */
|
599 |
+
__( 'A payment of %1$s was <strong>started</strong> using WooCommerce Payments (<code>%2$s</code>).', 'woocommerce-payments' ),
|
600 |
+
[
|
601 |
+
'strong' => '<strong>',
|
602 |
+
'code' => '<code>',
|
603 |
+
]
|
604 |
+
),
|
605 |
+
wc_price( $amount ),
|
606 |
+
$intent_id
|
607 |
+
);
|
608 |
+
$order->add_order_note( $note );
|
609 |
|
610 |
+
$order->update_meta_data( '_intent_id', $intent_id );
|
611 |
+
$order->update_meta_data( '_intention_status', $status );
|
612 |
+
$order->save();
|
613 |
+
|
614 |
+
return [
|
615 |
+
'result' => 'success',
|
616 |
+
// Include a new nonce for update_order_status to ensure the update order
|
617 |
+
// status call works when a guest user creates an account during checkout.
|
618 |
+
'redirect' => sprintf(
|
619 |
+
'#wcpay-confirm-pi:%s:%s:%s',
|
620 |
+
$order_id,
|
621 |
+
$intent->get_client_secret(),
|
622 |
+
wp_create_nonce( 'wcpay_update_order_status_nonce' )
|
623 |
+
),
|
624 |
+
];
|
625 |
}
|
626 |
+
}
|
627 |
|
628 |
+
wc_reduce_stock_levels( $order_id );
|
629 |
+
if ( isset( $cart ) ) {
|
630 |
+
$cart->empty_cart();
|
631 |
}
|
632 |
|
633 |
+
return [
|
634 |
+
'result' => 'success',
|
635 |
+
'redirect' => $this->get_return_url( $order ),
|
636 |
+
];
|
637 |
+
}
|
638 |
|
639 |
+
/**
|
640 |
+
* Saves the payment token to the order.
|
641 |
+
*
|
642 |
+
* @param WC_Order $order The order.
|
643 |
+
* @param WC_Payment_Token $token The token to save.
|
644 |
+
*/
|
645 |
+
protected function add_token_to_order( $order, $token ) {
|
646 |
+
$order_tokens = $order->get_payment_tokens();
|
647 |
+
|
648 |
+
// This could lead to tokens being saved twice in an order's payment tokens, but it is needed so that shoppers
|
649 |
+
// may re-use a previous card for the same subscription, as we consider the last token to be the active one.
|
650 |
+
// We can't remove the previous entry for the token because WC_Order does not support removal of tokens [1] and
|
651 |
+
// we can't delete the token as it might be used somewhere else.
|
652 |
+
// [1] https://github.com/woocommerce/woocommerce/issues/11857.
|
653 |
+
if ( $token->get_id() !== end( $order_tokens ) ) {
|
654 |
+
$order->add_payment_token( $token );
|
655 |
+
}
|
656 |
}
|
657 |
|
658 |
/**
|
765 |
return ob_get_clean();
|
766 |
}
|
767 |
|
768 |
+
/**
|
769 |
+
* Generates markup for account statement descriptor field.
|
770 |
+
*
|
771 |
+
* @param string $key Field key.
|
772 |
+
* @param array $data Field data.
|
773 |
+
*
|
774 |
+
* @return string
|
775 |
+
*/
|
776 |
+
public function generate_account_statement_descriptor_html( $key, $data ) {
|
777 |
+
if ( ! $this->is_connected() ) {
|
778 |
+
return '';
|
779 |
+
}
|
780 |
+
|
781 |
+
return parent::generate_text_html( $key, $data );
|
782 |
+
}
|
783 |
+
|
784 |
+
/**
|
785 |
+
* Get option from DB or connected account.
|
786 |
+
*
|
787 |
+
* Overrides parent method to retrieve some options from connected account.
|
788 |
+
*
|
789 |
+
* @param string $key Option key.
|
790 |
+
* @param mixed $empty_value Value when empty.
|
791 |
+
* @return string The value specified for the option or a default value for the option.
|
792 |
+
*/
|
793 |
+
public function get_option( $key, $empty_value = null ) {
|
794 |
+
switch ( $key ) {
|
795 |
+
case 'account_statement_descriptor':
|
796 |
+
return $this->get_account_statement_descriptor();
|
797 |
+
default:
|
798 |
+
return parent::get_option( $key, $empty_value );
|
799 |
+
}
|
800 |
+
}
|
801 |
+
|
802 |
+
/**
|
803 |
+
* Sanitizes plugin settings before saving them in site's DB.
|
804 |
+
*
|
805 |
+
* Filters out some values stored in connected account.
|
806 |
+
*
|
807 |
+
* @param array $settings Plugin settings.
|
808 |
+
* @return array Sanitized settings.
|
809 |
+
*/
|
810 |
+
public function sanitize_plugin_settings( $settings ) {
|
811 |
+
if ( isset( $settings['account_statement_descriptor'] ) ) {
|
812 |
+
$this->update_statement_descriptor( $settings['account_statement_descriptor'] );
|
813 |
+
unset( $settings['account_statement_descriptor'] );
|
814 |
+
}
|
815 |
+
|
816 |
+
return $settings;
|
817 |
+
}
|
818 |
+
|
819 |
+
/**
|
820 |
+
* Gets connected account statement descriptor.
|
821 |
+
*
|
822 |
+
* @param mixed $empty_value Empty value to return when not connected or fails to fetch account descriptor.
|
823 |
+
*
|
824 |
+
* @return string Statement descriptor of default value.
|
825 |
+
*/
|
826 |
+
private function get_account_statement_descriptor( $empty_value = null ) {
|
827 |
+
try {
|
828 |
+
if ( ! $this->is_connected() ) {
|
829 |
+
return $empty_value;
|
830 |
+
}
|
831 |
+
|
832 |
+
return $this->account->get_statement_descriptor();
|
833 |
+
} catch ( Exception $e ) {
|
834 |
+
Logger::error( 'Failed to get account statement descriptor.' . $e );
|
835 |
+
return $empty_value;
|
836 |
+
}
|
837 |
+
}
|
838 |
+
|
839 |
+
/**
|
840 |
+
* Handles statement descriptor update when plugin settings saved.
|
841 |
+
*
|
842 |
+
* Adds error message to display in admin notices in case of failure.
|
843 |
+
*
|
844 |
+
* @param string $statement_descriptor Statement descriptor value.
|
845 |
+
*/
|
846 |
+
private function update_statement_descriptor( $statement_descriptor ) {
|
847 |
+
if ( empty( $statement_descriptor ) ) {
|
848 |
+
return;
|
849 |
+
}
|
850 |
+
|
851 |
+
$account_settings = [
|
852 |
+
'statement_descriptor' => $statement_descriptor,
|
853 |
+
];
|
854 |
+
$error_message = $this->account->update_stripe_account( $account_settings );
|
855 |
+
|
856 |
+
if ( is_string( $error_message ) ) {
|
857 |
+
$msg = __( 'Failed to update Statement descriptor. ', 'woocommerce-payments' ) . $error_message;
|
858 |
+
$this->add_error( $msg );
|
859 |
+
}
|
860 |
+
}
|
861 |
+
|
862 |
+
/**
|
863 |
+
* Validates statement descriptor value
|
864 |
+
*
|
865 |
+
* @param string $key Field key.
|
866 |
+
* @param string $value Posted Value.
|
867 |
+
*
|
868 |
+
* @return string Sanitized statement descriptor.
|
869 |
+
* @throws Exception When statement descriptor is invalid.
|
870 |
+
*/
|
871 |
+
public function validate_account_statement_descriptor_field( $key, $value ) {
|
872 |
+
// Validation can be done with a single regex but splitting into multiple for better readability.
|
873 |
+
$valid_length = '/^.{5,22}$/';
|
874 |
+
$has_one_letter = '/^.*[a-zA-Z]+/';
|
875 |
+
$no_specials = '/^[^*"\'<>]*$/';
|
876 |
+
|
877 |
+
if (
|
878 |
+
! preg_match( $valid_length, $value ) ||
|
879 |
+
! preg_match( $has_one_letter, $value ) ||
|
880 |
+
! preg_match( $no_specials, $value )
|
881 |
+
) {
|
882 |
+
throw new Exception( __( 'Customer bank statement is invalid. Statement should be between 5 and 22 characters long, contain at least single Latin character and does not contain special characters: \' " * < >', 'woocommerce-payments' ) );
|
883 |
+
}
|
884 |
+
|
885 |
+
// Perform text validation after own checks to prevent special characters like < > escaped before own validation.
|
886 |
+
return $this->validate_text_field( $key, $value );
|
887 |
+
}
|
888 |
+
|
889 |
/**
|
890 |
* Generate markup for account actions
|
891 |
*/
|
1164 |
$intent_id
|
1165 |
);
|
1166 |
$order->add_order_note( $note );
|
1167 |
+
|
1168 |
+
// The order is successful, so update it to reflect that.
|
1169 |
+
$order->update_meta_data( '_charge_id', $intent->get_charge_id() );
|
1170 |
+
$order->update_meta_data( '_intention_status', $status );
|
1171 |
+
$order->save();
|
1172 |
+
|
1173 |
$order->payment_complete( $intent_id );
|
1174 |
break;
|
1175 |
case 'requires_capture':
|
1188 |
// Save the note separately because if there is no change in status
|
1189 |
// then the note is not saved using WC_Order::update_status.
|
1190 |
$order->add_order_note( $note );
|
1191 |
+
|
1192 |
+
// The order is successful, so update it to reflect that.
|
1193 |
+
$order->update_meta_data( '_charge_id', $intent->get_charge_id() );
|
1194 |
+
$order->update_meta_data( '_intention_status', $status );
|
1195 |
+
$order->save();
|
1196 |
+
|
1197 |
$order->update_status( 'on-hold' );
|
1198 |
$order->set_transaction_id( $intent_id );
|
1199 |
break;
|
1218 |
}
|
1219 |
|
1220 |
if ( 'succeeded' === $status || 'requires_capture' === $status ) {
|
|
|
|
|
|
|
|
|
|
|
1221 |
wc_reduce_stock_levels( $order_id );
|
1222 |
WC()->cart->empty_cart();
|
1223 |
|
1224 |
if ( ! empty( $payment_method_id ) ) {
|
1225 |
try {
|
1226 |
+
// TODO: Add token to subscriptions related to this order.
|
1227 |
$this->token_service->add_payment_method_to_user( $payment_method_id, wp_get_current_user() );
|
1228 |
} catch ( Exception $e ) {
|
1229 |
// If saving the token fails, log the error message but catch the error to avoid crashing the checkout flow.
|
1289 |
public function add_payment_method() {
|
1290 |
try {
|
1291 |
|
1292 |
+
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
1293 |
if ( ! isset( $_POST['wcpay-setup-intent'] ) ) {
|
1294 |
throw new Exception( __( 'A WooCommerce Payments payment method was not provided', 'woocommerce-payments' ) );
|
1295 |
}
|
1296 |
|
1297 |
+
// phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
|
1298 |
$setup_intent_id = ! empty( $_POST['wcpay-setup-intent'] ) ? wc_clean( $_POST['wcpay-setup-intent'] ) : false;
|
|
|
1299 |
|
1300 |
$customer_id = $this->customer_service->get_customer_id_by_user_id( get_current_user_id() );
|
1301 |
|
1331 |
* @throws Exception - When an error occurs in setup intent creation.
|
1332 |
*/
|
1333 |
public function create_setup_intent() {
|
1334 |
+
// phpcs:ignore WordPress.Security.NonceVerification.Missing
|
1335 |
+
$payment_information = Payment_Information::from_payment_request( $_POST );
|
1336 |
|
1337 |
// Determine the customer adding the payment method, create one if we don't have one already.
|
1338 |
$user = wp_get_current_user();
|
1342 |
}
|
1343 |
|
1344 |
return $this->payments_api_client->create_setup_intent(
|
1345 |
+
$payment_information->get_payment_method(),
|
1346 |
$customer_id
|
1347 |
);
|
1348 |
}
|
1373 |
);
|
1374 |
}
|
1375 |
}
|
1376 |
+
|
1377 |
+
/**
|
1378 |
+
* Add a url to the admin order page that links directly to the transactions detail view.
|
1379 |
+
*
|
1380 |
+
* @since 1.4.0
|
1381 |
+
*
|
1382 |
+
* @param WC_Order $order The context passed into this function when the user view the order details page in WordPress admin.
|
1383 |
+
* @return string
|
1384 |
+
*/
|
1385 |
+
public function get_transaction_url( $order ) {
|
1386 |
+
$charge_id = $order->get_meta( '_charge_id' );
|
1387 |
+
|
1388 |
+
if ( empty( $charge_id ) ) {
|
1389 |
+
return '';
|
1390 |
+
}
|
1391 |
+
|
1392 |
+
return add_query_arg(
|
1393 |
+
[
|
1394 |
+
'page' => 'wc-admin',
|
1395 |
+
'path' => '/payments/transactions/details&',
|
1396 |
+
'id' => $charge_id,
|
1397 |
+
],
|
1398 |
+
admin_url( 'admin.php' )
|
1399 |
+
);
|
1400 |
+
}
|
1401 |
}
|
includes/class-wc-payments-account.php
CHANGED
@@ -162,6 +162,17 @@ class WC_Payments_Account {
|
|
162 |
];
|
163 |
}
|
164 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
/**
|
166 |
* Utility function to immediately redirect to the main "Welcome to WooCommerce Payments" onboarding page.
|
167 |
* Note that this function immediately ends the execution.
|
@@ -226,6 +237,11 @@ class WC_Payments_Account {
|
|
226 |
return false;
|
227 |
}
|
228 |
|
|
|
|
|
|
|
|
|
|
|
229 |
return true;
|
230 |
}
|
231 |
|
@@ -567,7 +583,7 @@ class WC_Payments_Account {
|
|
567 |
delete_transient( self::ACCOUNT_TRANSIENT );
|
568 |
$this->get_cached_account_data();
|
569 |
} catch ( Exception $e ) {
|
570 |
-
|
571 |
}
|
572 |
}
|
573 |
|
@@ -617,4 +633,44 @@ class WC_Payments_Account {
|
|
617 |
}
|
618 |
);
|
619 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
620 |
}
|
162 |
];
|
163 |
}
|
164 |
|
165 |
+
/**
|
166 |
+
* Gets the account statement descriptor for rendering on the settings page.
|
167 |
+
*
|
168 |
+
* @return string Account statement descriptor.
|
169 |
+
* @throws WC_Payments_API_Exception Bubbles up from get_cached_account_data.
|
170 |
+
*/
|
171 |
+
public function get_statement_descriptor() {
|
172 |
+
$account = $this->get_cached_account_data();
|
173 |
+
return isset( $account['statement_descriptor'] ) ? $account['statement_descriptor'] : '';
|
174 |
+
}
|
175 |
+
|
176 |
/**
|
177 |
* Utility function to immediately redirect to the main "Welcome to WooCommerce Payments" onboarding page.
|
178 |
* Note that this function immediately ends the execution.
|
237 |
return false;
|
238 |
}
|
239 |
|
240 |
+
// Don't redirect if the user is on Jetpack pages.
|
241 |
+
if ( 'jetpack' === $current_page ) {
|
242 |
+
return false;
|
243 |
+
}
|
244 |
+
|
245 |
return true;
|
246 |
}
|
247 |
|
583 |
delete_transient( self::ACCOUNT_TRANSIENT );
|
584 |
$this->get_cached_account_data();
|
585 |
} catch ( Exception $e ) {
|
586 |
+
Logger::error( "Failed to refresh account data. Error: $e" );
|
587 |
}
|
588 |
}
|
589 |
|
633 |
}
|
634 |
);
|
635 |
}
|
636 |
+
|
637 |
+
/**
|
638 |
+
* Updates Stripe account settings.
|
639 |
+
*
|
640 |
+
* @param array $stripe_account_settings Settings to update.
|
641 |
+
*
|
642 |
+
* @return null|string Error message if update failed.
|
643 |
+
*/
|
644 |
+
public function update_stripe_account( $stripe_account_settings ) {
|
645 |
+
try {
|
646 |
+
if ( ! $this->settings_changed( $stripe_account_settings ) ) {
|
647 |
+
Logger::info( 'Skip updating account settings. Nothing is changed.' );
|
648 |
+
return;
|
649 |
+
}
|
650 |
+
$updated_account = $this->payments_api_client->update_account( $stripe_account_settings );
|
651 |
+
$this->cache_account( $updated_account );
|
652 |
+
} catch ( Exception $e ) {
|
653 |
+
Logger::error( 'Failed to update Stripe account ' . $e );
|
654 |
+
return $e->getMessage();
|
655 |
+
}
|
656 |
+
}
|
657 |
+
|
658 |
+
/**
|
659 |
+
* Checks if account settings changed.
|
660 |
+
*
|
661 |
+
* @param array $changes Account settings changes.
|
662 |
+
*
|
663 |
+
* @return bool True if at least one parameter value is changed.
|
664 |
+
*/
|
665 |
+
private function settings_changed( $changes = [] ) {
|
666 |
+
$account = get_transient( self::ACCOUNT_TRANSIENT );
|
667 |
+
|
668 |
+
// Consider changes as valid if we don't have cached account data.
|
669 |
+
if ( ! $this->is_valid_cached_account( $account ) ) {
|
670 |
+
return true;
|
671 |
+
}
|
672 |
+
|
673 |
+
$diff = array_diff_assoc( $changes, $account );
|
674 |
+
return ! empty( $diff );
|
675 |
+
}
|
676 |
}
|
includes/class-wc-payments-token-service.php
CHANGED
@@ -51,6 +51,7 @@ class WC_Payments_Token_Service {
|
|
51 |
*
|
52 |
* @param array $payment_method Payment method to be added.
|
53 |
* @param WP_User $user User to attach payment method to.
|
|
|
54 |
*/
|
55 |
public function add_token_to_user( $payment_method, $user ) {
|
56 |
// Clear cached payment methods.
|
@@ -74,10 +75,11 @@ class WC_Payments_Token_Service {
|
|
74 |
*
|
75 |
* @param string $payment_method_id Payment method to be added.
|
76 |
* @param WP_User $user User to attach payment method to.
|
|
|
77 |
*/
|
78 |
public function add_payment_method_to_user( $payment_method_id, $user ) {
|
79 |
$payment_method_object = $this->payments_api_client->get_payment_method( $payment_method_id );
|
80 |
-
return $this->add_token_to_user( $payment_method_object,
|
81 |
}
|
82 |
|
83 |
/**
|
@@ -93,7 +95,7 @@ class WC_Payments_Token_Service {
|
|
93 |
return $tokens;
|
94 |
}
|
95 |
|
96 |
-
$customer_id = $this->customer_service->get_customer_id_by_user_id(
|
97 |
|
98 |
if ( null === $customer_id ) {
|
99 |
return $tokens;
|
@@ -110,7 +112,7 @@ class WC_Payments_Token_Service {
|
|
110 |
foreach ( $payment_methods as $payment_method ) {
|
111 |
if ( isset( $payment_method['type'] ) && 'card' === $payment_method['type'] ) {
|
112 |
if ( ! in_array( $payment_method['id'], $stored_tokens, true ) ) {
|
113 |
-
$token = $this->add_token_to_user( $payment_method,
|
114 |
$tokens[ $token->get_id() ] = $token;
|
115 |
}
|
116 |
}
|
@@ -145,7 +147,7 @@ class WC_Payments_Token_Service {
|
|
145 |
*/
|
146 |
public function woocommerce_payment_token_set_default( $token_id, $token ) {
|
147 |
if ( WC_Payment_Gateway_WCPay::GATEWAY_ID === $token->get_gateway_id() ) {
|
148 |
-
$customer_id = $this->customer_service->get_customer_id_by_user_id(
|
149 |
if ( $customer_id ) {
|
150 |
$this->customer_service->set_default_payment_method_for_customer( $customer_id, $token->get_token() );
|
151 |
// Clear cached payment methods.
|
51 |
*
|
52 |
* @param array $payment_method Payment method to be added.
|
53 |
* @param WP_User $user User to attach payment method to.
|
54 |
+
* @return WC_Payment_Token_CC The WC object for the payment token.
|
55 |
*/
|
56 |
public function add_token_to_user( $payment_method, $user ) {
|
57 |
// Clear cached payment methods.
|
75 |
*
|
76 |
* @param string $payment_method_id Payment method to be added.
|
77 |
* @param WP_User $user User to attach payment method to.
|
78 |
+
* @return WC_Payment_Token_CC The newly created token.
|
79 |
*/
|
80 |
public function add_payment_method_to_user( $payment_method_id, $user ) {
|
81 |
$payment_method_object = $this->payments_api_client->get_payment_method( $payment_method_id );
|
82 |
+
return $this->add_token_to_user( $payment_method_object, $user );
|
83 |
}
|
84 |
|
85 |
/**
|
95 |
return $tokens;
|
96 |
}
|
97 |
|
98 |
+
$customer_id = $this->customer_service->get_customer_id_by_user_id( $user_id );
|
99 |
|
100 |
if ( null === $customer_id ) {
|
101 |
return $tokens;
|
112 |
foreach ( $payment_methods as $payment_method ) {
|
113 |
if ( isset( $payment_method['type'] ) && 'card' === $payment_method['type'] ) {
|
114 |
if ( ! in_array( $payment_method['id'], $stored_tokens, true ) ) {
|
115 |
+
$token = $this->add_token_to_user( $payment_method, get_user_by( 'id', $user_id ) );
|
116 |
$tokens[ $token->get_id() ] = $token;
|
117 |
}
|
118 |
}
|
147 |
*/
|
148 |
public function woocommerce_payment_token_set_default( $token_id, $token ) {
|
149 |
if ( WC_Payment_Gateway_WCPay::GATEWAY_ID === $token->get_gateway_id() ) {
|
150 |
+
$customer_id = $this->customer_service->get_customer_id_by_user_id( $token->get_user_id() );
|
151 |
if ( $customer_id ) {
|
152 |
$this->customer_service->set_default_payment_method_for_customer( $customer_id, $token->get_token() );
|
153 |
// Clear cached payment methods.
|
includes/class-wc-payments.php
CHANGED
@@ -94,13 +94,20 @@ class WC_Payments {
|
|
94 |
include_once dirname( __FILE__ ) . '/class-logger.php';
|
95 |
include_once dirname( __FILE__ ) . '/class-wc-payment-gateway-wcpay.php';
|
96 |
include_once dirname( __FILE__ ) . '/class-wc-payments-token-service.php';
|
97 |
-
include_once
|
|
|
98 |
|
99 |
self::$account = new WC_Payments_Account( self::$api_client );
|
100 |
self::$customer_service = new WC_Payments_Customer_Service( self::$api_client );
|
101 |
self::$token_service = new WC_Payments_Token_Service( self::$api_client, self::$customer_service );
|
102 |
|
103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
add_filter( 'woocommerce_payment_gateways', [ __CLASS__, 'register_gateway' ] );
|
106 |
add_filter( 'option_woocommerce_gateway_order', [ __CLASS__, 'set_gateway_top_of_list' ], 2 );
|
94 |
include_once dirname( __FILE__ ) . '/class-logger.php';
|
95 |
include_once dirname( __FILE__ ) . '/class-wc-payment-gateway-wcpay.php';
|
96 |
include_once dirname( __FILE__ ) . '/class-wc-payments-token-service.php';
|
97 |
+
include_once dirname( __FILE__ ) . '/exceptions/class-wc-payments-intent-authentication-exception.php';
|
98 |
+
include_once dirname( __FILE__ ) . '/data-types/class-payment-information.php';
|
99 |
|
100 |
self::$account = new WC_Payments_Account( self::$api_client );
|
101 |
self::$customer_service = new WC_Payments_Customer_Service( self::$api_client );
|
102 |
self::$token_service = new WC_Payments_Token_Service( self::$api_client, self::$customer_service );
|
103 |
|
104 |
+
$gateway_class = 'WC_Payment_Gateway_WCPay';
|
105 |
+
if ( class_exists( 'WC_Subscriptions' ) && version_compare( WC_Subscriptions::$version, '3.0.0', '>=' ) ) {
|
106 |
+
include_once dirname( __FILE__ ) . '/compat/subscriptions/class-wc-payment-gateway-wcpay-subscriptions-compat.php';
|
107 |
+
$gateway_class = 'WC_Payment_Gateway_WCPay_Subscriptions_Compat';
|
108 |
+
}
|
109 |
+
|
110 |
+
self::$gateway = new $gateway_class( self::$api_client, self::$account, self::$customer_service, self::$token_service );
|
111 |
|
112 |
add_filter( 'woocommerce_payment_gateways', [ __CLASS__, 'register_gateway' ] );
|
113 |
add_filter( 'option_woocommerce_gateway_order', [ __CLASS__, 'set_gateway_top_of_list' ], 2 );
|
includes/compat/subscriptions/class-wc-payment-gateway-wcpay-subscriptions-compat.php
ADDED
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class WC_Payment_Gateway_WCPay_Subscriptions_Compat
|
4 |
+
*
|
5 |
+
* @package WooCommerce\Payments
|
6 |
+
*/
|
7 |
+
|
8 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
9 |
+
exit; // Exit if accessed directly.
|
10 |
+
}
|
11 |
+
|
12 |
+
use WCPay\Logger;
|
13 |
+
use WCPay\DataTypes\Payment_Information;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Gateway class for WooCommerce Payments, with added compatibility with WooCommerce Subscriptions.
|
17 |
+
*/
|
18 |
+
class WC_Payment_Gateway_WCPay_Subscriptions_Compat extends WC_Payment_Gateway_WCPay {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* WC_Payment_Gateway_WCPay_Subscriptions_Compat constructor.
|
22 |
+
*
|
23 |
+
* @param array ...$args Arguments passed to the main gateway's constructor.
|
24 |
+
*/
|
25 |
+
public function __construct( ...$args ) {
|
26 |
+
parent::__construct( ...$args );
|
27 |
+
|
28 |
+
$this->supports = array_merge(
|
29 |
+
$this->supports,
|
30 |
+
[
|
31 |
+
'subscriptions',
|
32 |
+
'subscription_cancellation',
|
33 |
+
'subscription_suspension',
|
34 |
+
'subscription_reactivation',
|
35 |
+
'subscription_amount_changes',
|
36 |
+
'subscription_date_changes',
|
37 |
+
'subscription_payment_method_change',
|
38 |
+
'subscription_payment_method_change_customer',
|
39 |
+
'multiple_subscriptions',
|
40 |
+
]
|
41 |
+
);
|
42 |
+
|
43 |
+
add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, [ $this, 'scheduled_subscription_payment' ], 10, 2 );
|
44 |
+
add_action( 'woocommerce_subscription_failing_payment_method_updated_' . $this->id, [ $this, 'update_failing_payment_method' ], 10, 2 );
|
45 |
+
add_filter( 'wc_payments_display_save_payment_method_checkbox', [ $this, 'display_save_payment_method_checkbox' ], 10 );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Process the payment for a given order.
|
50 |
+
*
|
51 |
+
* @param int $order_id Order ID to process the payment for.
|
52 |
+
* @param bool $is_recurring_payment Whether this is a one-off payment (false) or it's the first installment of a recurring payment (true).
|
53 |
+
*
|
54 |
+
* @return array|null An array with result of payment and redirect URL, or nothing.
|
55 |
+
*/
|
56 |
+
public function process_payment( $order_id, $is_recurring_payment = false ) {
|
57 |
+
return parent::process_payment( $order_id, wcs_order_contains_subscription( $order_id ) );
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Returns a boolean value indicating whether the save payment checkbox should be
|
62 |
+
* displayed during checkout.
|
63 |
+
*
|
64 |
+
* Returns `false` if the cart currently has a subscriptions. Returns the value in
|
65 |
+
* `$display` otherwise.
|
66 |
+
*
|
67 |
+
* @param bool $display Bool indicating whether to show the save payment checkbox in the absence of subscriptions.
|
68 |
+
*
|
69 |
+
* @return bool Indicates whether the save payment method checkbox should be displayed or not.
|
70 |
+
*/
|
71 |
+
public function display_save_payment_method_checkbox( $display ) {
|
72 |
+
if ( WC_Subscriptions_Cart::cart_contains_subscription() ) {
|
73 |
+
return false;
|
74 |
+
}
|
75 |
+
// Only render the "Save payment method" checkbox if there are no subscription products in the cart.
|
76 |
+
return $display;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Process a scheduled subscription payment.
|
81 |
+
*
|
82 |
+
* @param float $amount The amount to charge.
|
83 |
+
* @param WC_Order $renewal_order A WC_Order object created to record the renewal payment.
|
84 |
+
*/
|
85 |
+
public function scheduled_subscription_payment( $amount, $renewal_order ) {
|
86 |
+
$order_tokens = $renewal_order->get_payment_tokens();
|
87 |
+
$token_id = end( $order_tokens );
|
88 |
+
$token = ! $token_id ? null : WC_Payment_Tokens::get( $token_id );
|
89 |
+
if ( is_null( $token ) ) {
|
90 |
+
Logger::error( 'There is no saved payment token for order #' . $renewal_order->get_id() );
|
91 |
+
$renewal_order->update_status( 'failed' );
|
92 |
+
return;
|
93 |
+
}
|
94 |
+
|
95 |
+
$payment_information = new Payment_Information( '', $token, true );
|
96 |
+
|
97 |
+
try {
|
98 |
+
// TODO: make `force_saved_card` and adding the 'recurring' metadata 2 distinct features.
|
99 |
+
$this->process_payment_for_order( $renewal_order, null, $payment_information, false, true );
|
100 |
+
} catch ( WC_Payments_API_Exception $e ) {
|
101 |
+
Logger::error( 'Error processing subscription renewal: ' . $e->getMessage() );
|
102 |
+
|
103 |
+
$renewal_order->update_status( 'failed' );
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Adds the payment token from a failed renewal order to the provided subscription.
|
109 |
+
*
|
110 |
+
* @param WC_Subscription $subscription The subscription to be updated.
|
111 |
+
* @param WC_Order $renewal_order The failed renewal order.
|
112 |
+
*/
|
113 |
+
public function update_failing_payment_method( $subscription, $renewal_order ) {
|
114 |
+
$renewal_order_tokens = $renewal_order->get_payment_tokens();
|
115 |
+
$renewal_token_id = end( $renewal_order_tokens );
|
116 |
+
$renewal_token = ! $renewal_token_id ? null : WC_Payment_Tokens::get( $renewal_token_id );
|
117 |
+
if ( is_null( $renewal_token ) ) {
|
118 |
+
Logger::error( 'Failing subscription could not be updated: there is no saved payment token for order #' . $renewal_order->get_id() );
|
119 |
+
return;
|
120 |
+
}
|
121 |
+
$subscription->add_payment_token( $renewal_token );
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Saves the payment token to the order.
|
126 |
+
*
|
127 |
+
* @param WC_Order $order The order.
|
128 |
+
* @param WC_Payment_Token $token The token to save.
|
129 |
+
*/
|
130 |
+
protected function add_token_to_order( $order, $token ) {
|
131 |
+
parent::add_token_to_order( $order, $token );
|
132 |
+
|
133 |
+
// Set payment token for subscriptions, so it can be used for renewals.
|
134 |
+
$subscriptions = wcs_get_subscriptions_for_order( $order->get_id() );
|
135 |
+
foreach ( $subscriptions as $subscription ) {
|
136 |
+
parent::add_token_to_order( $subscription, $token );
|
137 |
+
}
|
138 |
+
}
|
139 |
+
}
|
includes/data-types/class-payment-information.php
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Class Payment_Information
|
4 |
+
*
|
5 |
+
* @package WooCommerce\Payments
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace WCPay\DataTypes;
|
9 |
+
|
10 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
11 |
+
exit; // Exit if accessed directly.
|
12 |
+
}
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Mostly a wrapper containing information on a single payment.
|
16 |
+
*/
|
17 |
+
class Payment_Information {
|
18 |
+
/**
|
19 |
+
* The ID of the payment method used for this payment.
|
20 |
+
*
|
21 |
+
* @var string
|
22 |
+
*/
|
23 |
+
private $payment_method;
|
24 |
+
|
25 |
+
/**
|
26 |
+
* The payment token used for this payment.
|
27 |
+
*
|
28 |
+
* @var \WC_Payment_Token/NULL
|
29 |
+
*/
|
30 |
+
private $token;
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Indicates whether the payment is merchant-initiated (true) or customer-initiated (false).
|
34 |
+
*
|
35 |
+
* @var bool
|
36 |
+
*/
|
37 |
+
private $off_session;
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Payment information constructor.
|
41 |
+
*
|
42 |
+
* @param string $payment_method The ID of the payment method used for this payment.
|
43 |
+
* @param \WC_Payment_Token $token The payment token used for this payment.
|
44 |
+
* @param bool $off_session Indicates whether the payment is merchant-initiated (true) or customer-initiated (false).
|
45 |
+
*
|
46 |
+
* @throws \Exception - If no payment method is found in the provided request.
|
47 |
+
*/
|
48 |
+
public function __construct(
|
49 |
+
string $payment_method,
|
50 |
+
\WC_Payment_Token $token = null,
|
51 |
+
bool $off_session = false
|
52 |
+
) {
|
53 |
+
if ( empty( $payment_method ) && empty( $token ) ) {
|
54 |
+
throw new \Exception( __( 'Invalid payment method. Please input a new card number.', 'woocommerce-payments' ) );
|
55 |
+
}
|
56 |
+
|
57 |
+
$this->payment_method = $payment_method;
|
58 |
+
$this->token = $token;
|
59 |
+
$this->off_session = $off_session;
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
* Returns true if payment was initiated by the merchant, false otherwise.
|
64 |
+
*
|
65 |
+
* @return bool True if payment was initiated by the merchant, false otherwise.
|
66 |
+
*/
|
67 |
+
public function is_merchant_initiated(): bool {
|
68 |
+
return $this->off_session;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Returns the payment method ID.
|
73 |
+
*
|
74 |
+
* @return string The payment method ID.
|
75 |
+
*/
|
76 |
+
public function get_payment_method(): string {
|
77 |
+
// Use the token if we have it.
|
78 |
+
if ( $this->is_using_saved_payment_method() ) {
|
79 |
+
return $this->token->get_token();
|
80 |
+
}
|
81 |
+
|
82 |
+
return $this->payment_method;
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Returns the payment token.
|
87 |
+
*
|
88 |
+
* TODO: Once php requirement is bumped to >= 7.1.0 change return type to ?\WC_Payment_Token
|
89 |
+
* since the return type is nullable, as per
|
90 |
+
* https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration
|
91 |
+
*
|
92 |
+
* @return \WC_Payment_Token/NULL The payment token.
|
93 |
+
*/
|
94 |
+
public function get_payment_token(): \WC_Payment_Token {
|
95 |
+
return $this->token;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* Update the payment token associated with this payment.
|
100 |
+
*
|
101 |
+
* @param \WC_Payment_Token $token The new payment token.
|
102 |
+
*/
|
103 |
+
public function set_token( \WC_Payment_Token $token ) {
|
104 |
+
$this->token = $token;
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Returns true if the payment token is not empty, false otherwise.
|
109 |
+
*
|
110 |
+
* @return bool True if payment token is not empty, false otherwise.
|
111 |
+
*/
|
112 |
+
public function is_using_saved_payment_method(): bool {
|
113 |
+
return ! empty( $this->token );
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Payment information constructor.
|
118 |
+
*
|
119 |
+
* @param array $request Associative array containing payment request information.
|
120 |
+
* @param bool $off_session Indicates whether the payment is merchant-initiated (true) or customer-initiated (false).
|
121 |
+
*
|
122 |
+
* @throws Exception - If no payment method is found in the provided request.
|
123 |
+
*/
|
124 |
+
public static function from_payment_request(
|
125 |
+
array $request,
|
126 |
+
bool $off_session = false
|
127 |
+
): Payment_Information {
|
128 |
+
$payment_method = self::get_payment_method_from_request( $request );
|
129 |
+
$token = self::get_token_from_request( $request );
|
130 |
+
$off_session = $off_session;
|
131 |
+
|
132 |
+
return new Payment_Information( $payment_method, $token, $off_session );
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Extracts the payment method from the provided request.
|
137 |
+
*
|
138 |
+
* @param array $request Associative array containing payment request information.
|
139 |
+
*
|
140 |
+
* @return string
|
141 |
+
*/
|
142 |
+
public static function get_payment_method_from_request( array $request ): string {
|
143 |
+
//phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
|
144 |
+
return ! empty( $request['wcpay-payment-method'] ) ? wc_clean( $request['wcpay-payment-method'] ) : '';
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Extract the payment token from the provided request.
|
149 |
+
*
|
150 |
+
* TODO: Once php requirement is bumped to >= 7.1.0 set return type to ?\WC_Payment_Token
|
151 |
+
* since the return type is nullable, as per
|
152 |
+
* https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration
|
153 |
+
*
|
154 |
+
* @param array $request Associative array containing payment request information.
|
155 |
+
*
|
156 |
+
* @return \WC_Payment_Token|NULL
|
157 |
+
*/
|
158 |
+
public static function get_token_from_request( array $request ) {
|
159 |
+
if (
|
160 |
+
! isset( $request[ 'wc-' . \WC_Payment_Gateway_WCPay::GATEWAY_ID . '-payment-token' ] ) ||
|
161 |
+
'new' === $request[ 'wc-' . \WC_Payment_Gateway_WCPay::GATEWAY_ID . '-payment-token' ]
|
162 |
+
) {
|
163 |
+
return null;
|
164 |
+
}
|
165 |
+
|
166 |
+
//phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
|
167 |
+
$token = \WC_Payment_Tokens::get( wc_clean( $request[ 'wc-' . \WC_Payment_Gateway_WCPay::GATEWAY_ID . '-payment-token' ] ) );
|
168 |
+
|
169 |
+
// If the token doesn't belong to this gateway or the current user it's invalid.
|
170 |
+
if ( ! $token || \WC_Payment_Gateway_WCPay::GATEWAY_ID !== $token->get_gateway_id() || $token->get_user_id() !== get_current_user_id() ) {
|
171 |
+
return null;
|
172 |
+
}
|
173 |
+
|
174 |
+
return $token;
|
175 |
+
}
|
176 |
+
}
|
includes/wc-payment-api/class-wc-payments-api-client.php
CHANGED
@@ -121,6 +121,7 @@ class WC_Payments_API_Client {
|
|
121 |
* @param bool $save_payment_method - Whether to save payment method for future purchases.
|
122 |
* @param array $metadata - Meta data values to be sent along with payment intent creation.
|
123 |
* @param array $level3 - Level 3 data.
|
|
|
124 |
*
|
125 |
* @return WC_Payments_API_Intention
|
126 |
* @throws WC_Payments_API_Exception - Exception thrown on intention creation failure.
|
@@ -133,7 +134,8 @@ class WC_Payments_API_Client {
|
|
133 |
$manual_capture = false,
|
134 |
$save_payment_method = false,
|
135 |
$metadata = [],
|
136 |
-
$level3 = []
|
|
|
137 |
) {
|
138 |
// TODO: There's scope to have amount and currency bundled up into an object.
|
139 |
$request = [];
|
@@ -146,6 +148,10 @@ class WC_Payments_API_Client {
|
|
146 |
$request['metadata'] = $metadata;
|
147 |
$request['level3'] = $level3;
|
148 |
|
|
|
|
|
|
|
|
|
149 |
if ( $save_payment_method ) {
|
150 |
$request['setup_future_usage'] = 'off_session';
|
151 |
}
|
@@ -254,14 +260,16 @@ class WC_Payments_API_Client {
|
|
254 |
*
|
255 |
* @param string $payment_method_id - ID of payment method to be saved.
|
256 |
* @param string $customer_id - ID of the customer.
|
|
|
257 |
*
|
258 |
* @return array
|
259 |
* @throws WC_Payments_API_Exception - Exception thrown on setup intent creation failure.
|
260 |
*/
|
261 |
-
public function create_setup_intent( $payment_method_id, $customer_id ) {
|
262 |
$request = [
|
263 |
'payment_method' => $payment_method_id,
|
264 |
'customer' => $customer_id,
|
|
|
265 |
];
|
266 |
|
267 |
return $this->request( $request, self::SETUP_INTENTS_API, self::POST );
|
@@ -612,6 +620,21 @@ class WC_Payments_API_Client {
|
|
612 |
);
|
613 |
}
|
614 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
615 |
/**
|
616 |
* Get data needed to initialize the OAuth flow
|
617 |
*
|
121 |
* @param bool $save_payment_method - Whether to save payment method for future purchases.
|
122 |
* @param array $metadata - Meta data values to be sent along with payment intent creation.
|
123 |
* @param array $level3 - Level 3 data.
|
124 |
+
* @param bool $off_session - Whether the payment is off-session (merchant-initiated), or on-session (customer-initiated).
|
125 |
*
|
126 |
* @return WC_Payments_API_Intention
|
127 |
* @throws WC_Payments_API_Exception - Exception thrown on intention creation failure.
|
134 |
$manual_capture = false,
|
135 |
$save_payment_method = false,
|
136 |
$metadata = [],
|
137 |
+
$level3 = [],
|
138 |
+
$off_session = false
|
139 |
) {
|
140 |
// TODO: There's scope to have amount and currency bundled up into an object.
|
141 |
$request = [];
|
148 |
$request['metadata'] = $metadata;
|
149 |
$request['level3'] = $level3;
|
150 |
|
151 |
+
if ( $off_session ) {
|
152 |
+
$request['off_session'] = true;
|
153 |
+
}
|
154 |
+
|
155 |
if ( $save_payment_method ) {
|
156 |
$request['setup_future_usage'] = 'off_session';
|
157 |
}
|
260 |
*
|
261 |
* @param string $payment_method_id - ID of payment method to be saved.
|
262 |
* @param string $customer_id - ID of the customer.
|
263 |
+
* @param bool $confirm - Flag to confirm the intent on creation if true.
|
264 |
*
|
265 |
* @return array
|
266 |
* @throws WC_Payments_API_Exception - Exception thrown on setup intent creation failure.
|
267 |
*/
|
268 |
+
public function create_setup_intent( $payment_method_id, $customer_id, $confirm = 'false' ) {
|
269 |
$request = [
|
270 |
'payment_method' => $payment_method_id,
|
271 |
'customer' => $customer_id,
|
272 |
+
'confirm' => $confirm,
|
273 |
];
|
274 |
|
275 |
return $this->request( $request, self::SETUP_INTENTS_API, self::POST );
|
620 |
);
|
621 |
}
|
622 |
|
623 |
+
/**
|
624 |
+
* Update Stripe account data
|
625 |
+
*
|
626 |
+
* @param array $stripe_account_settings Settings to update.
|
627 |
+
*
|
628 |
+
* @return array Updated account data.
|
629 |
+
*/
|
630 |
+
public function update_account( $stripe_account_settings ) {
|
631 |
+
return $this->request(
|
632 |
+
$stripe_account_settings,
|
633 |
+
self::ACCOUNTS_API,
|
634 |
+
self::POST
|
635 |
+
);
|
636 |
+
}
|
637 |
+
|
638 |
/**
|
639 |
* Get data needed to initialize the OAuth flow
|
640 |
*
|
includes/wc-payment-api/class-wc-payments-http.php
CHANGED
@@ -101,7 +101,7 @@ class WC_Payments_Http {
|
|
101 |
* @return bool true if Jetpack connection has access token.
|
102 |
*/
|
103 |
public function is_connected() {
|
104 |
-
return $this->connection_manager->is_active();
|
105 |
}
|
106 |
|
107 |
/**
|
@@ -113,15 +113,18 @@ class WC_Payments_Http {
|
|
113 |
* @throws WC_Payments_API_Exception - Exception thrown on failure.
|
114 |
*/
|
115 |
public function start_connection( $redirect ) {
|
116 |
-
//
|
117 |
-
|
|
|
|
|
|
|
118 |
$result = $this->connection_manager->register();
|
119 |
if ( is_wp_error( $result ) ) {
|
120 |
throw new WC_Payments_API_Exception( $result->get_error_message(), 'wcpay_jetpack_register_site_failed', 500 );
|
121 |
}
|
122 |
}
|
123 |
|
124 |
-
//
|
125 |
add_filter( 'jetpack_use_iframe_authorization_flow', '__return_false' );
|
126 |
// Same logic as in WC-Admin.
|
127 |
$calypso_env = defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, [ 'development', 'wpcalypso', 'horizon', 'stage' ], true ) ? WOOCOMMERCE_CALYPSO_ENVIRONMENT : 'production';
|
101 |
* @return bool true if Jetpack connection has access token.
|
102 |
*/
|
103 |
public function is_connected() {
|
104 |
+
return $this->connection_manager->is_plugin_enabled() && $this->connection_manager->is_active();
|
105 |
}
|
106 |
|
107 |
/**
|
113 |
* @throws WC_Payments_API_Exception - Exception thrown on failure.
|
114 |
*/
|
115 |
public function start_connection( $redirect ) {
|
116 |
+
// Mark the plugin as enabled in case it had been soft-disconnected.
|
117 |
+
$this->connection_manager->enable_plugin();
|
118 |
+
|
119 |
+
// Register the site to wp.com.
|
120 |
+
if ( ! $this->connection_manager->is_registered() ) {
|
121 |
$result = $this->connection_manager->register();
|
122 |
if ( is_wp_error( $result ) ) {
|
123 |
throw new WC_Payments_API_Exception( $result->get_error_message(), 'wcpay_jetpack_register_site_failed', 500 );
|
124 |
}
|
125 |
}
|
126 |
|
127 |
+
// Redirect the user to the Jetpack user connection flow.
|
128 |
add_filter( 'jetpack_use_iframe_authorization_flow', '__return_false' );
|
129 |
// Same logic as in WC-Admin.
|
130 |
$calypso_env = defined( 'WOOCOMMERCE_CALYPSO_ENVIRONMENT' ) && in_array( WOOCOMMERCE_CALYPSO_ENVIRONMENT, [ 'development', 'wpcalypso', 'horizon', 'stage' ], true ) ? WOOCOMMERCE_CALYPSO_ENVIRONMENT : 'production';
|
readme.txt
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
=== WooCommerce Payments ===
|
2 |
-
Contributors: automattic
|
3 |
Tags: woocommerce, payment, payment request, credit card, automattic
|
4 |
Requires at least: 5.3
|
5 |
Tested up to: 5.4
|
6 |
Requires PHP: 7.0
|
7 |
-
Stable tag: 1.
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
@@ -90,6 +90,14 @@ You can read our Terms of Service [here](https://en.wordpress.com/tos).
|
|
90 |
|
91 |
== Changelog ==
|
92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
= 1.3.0 - 2020-08-17 =
|
94 |
* Add - Support for saved cards.
|
95 |
* Add - Search bar for transactions.
|
1 |
=== WooCommerce Payments ===
|
2 |
+
Contributors: woocommerce, automattic
|
3 |
Tags: woocommerce, payment, payment request, credit card, automattic
|
4 |
Requires at least: 5.3
|
5 |
Tested up to: 5.4
|
6 |
Requires PHP: 7.0
|
7 |
+
Stable tag: 1.4.0
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
90 |
|
91 |
== Changelog ==
|
92 |
|
93 |
+
= 1.4.0 - 2020-09-02 =
|
94 |
+
* Add - Initial support for WooCommerce Subscriptions: Signing up for subscriptions, scheduled payments, and customer-initiated payment method changes.
|
95 |
+
* Add - Added a link to transaction details from order screens.
|
96 |
+
* Add - Allow merchant to edit statement descriptor.
|
97 |
+
* Fix - Do not redirect to the onboarding page after completing the WC4.5-beta wizard.
|
98 |
+
* Fix - Save order metadata before the payment is completed to avoid missing payments.
|
99 |
+
* Update - Bumped the minimum Jetpack requirement to version 8.2.
|
100 |
+
|
101 |
= 1.3.0 - 2020-08-17 =
|
102 |
* Add - Support for saved cards.
|
103 |
* Add - Search bar for transactions.
|
vendor/autoload.php
CHANGED
@@ -4,4 +4,4 @@
|
|
4 |
|
5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
|
7 |
-
return
|
4 |
|
5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
6 |
|
7 |
+
return ComposerAutoloaderInit0a651f92f6dec0685e6f92fb5fc9b1f9::getLoader();
|
vendor/autoload_packages.php
CHANGED
@@ -179,7 +179,7 @@ if ( ! function_exists( __NAMESPACE__ . '\autoloader' ) ) {
|
|
179 |
/**
|
180 |
* Prepare all the classes for autoloading.
|
181 |
*/
|
182 |
-
function
|
183 |
$class_map = require_once dirname( __FILE__ ) . '/composer/autoload_classmap_package.php';
|
184 |
foreach ( $class_map as $class_name => $class_info ) {
|
185 |
enqueue_package_class( $class_name, $class_info['version'], $class_info['path'] );
|
@@ -203,4 +203,4 @@ function enqueue_packages_996f9e0187d1cf069c402189f248054f() {
|
|
203 |
file_loader(); // Either WordPress is not loaded or plugin is doing it wrong. Either way we'll load the files so nothing breaks.
|
204 |
}
|
205 |
}
|
206 |
-
|
179 |
/**
|
180 |
* Prepare all the classes for autoloading.
|
181 |
*/
|
182 |
+
function enqueue_packages_1c17c81d6bb0e789d15762cdc7617239() {
|
183 |
$class_map = require_once dirname( __FILE__ ) . '/composer/autoload_classmap_package.php';
|
184 |
foreach ( $class_map as $class_name => $class_info ) {
|
185 |
enqueue_package_class( $class_name, $class_info['version'], $class_info['path'] );
|
203 |
file_loader(); // Either WordPress is not loaded or plugin is doing it wrong. Either way we'll load the files so nothing breaks.
|
204 |
}
|
205 |
}
|
206 |
+
enqueue_packages_1c17c81d6bb0e789d15762cdc7617239();
|
vendor/automattic/jetpack-config/src/class-config.php
CHANGED
@@ -8,7 +8,8 @@
|
|
8 |
namespace Automattic\Jetpack;
|
9 |
|
10 |
use Automattic\Jetpack\Connection\Manager;
|
11 |
-
use Automattic\Jetpack\JITMS\JITM;
|
|
|
12 |
use Automattic\Jetpack\Connection\Plugin;
|
13 |
use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
|
14 |
use Automattic\Jetpack\Sync\Main as Sync_Main;
|
@@ -91,8 +92,10 @@ class Config {
|
|
91 |
}
|
92 |
|
93 |
if ( $this->config['jitm'] ) {
|
94 |
-
|
95 |
-
|
|
|
|
|
96 |
}
|
97 |
}
|
98 |
|
@@ -100,13 +103,15 @@ class Config {
|
|
100 |
* Returns true if the required class is available and alerts the user if it's not available
|
101 |
* in case the site is in debug mode.
|
102 |
*
|
103 |
-
* @param String
|
|
|
|
|
104 |
* @return Boolean whether the class is available.
|
105 |
*/
|
106 |
-
protected function ensure_class( $classname ) {
|
107 |
$available = class_exists( $classname );
|
108 |
|
109 |
-
if ( ! $available && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
110 |
trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
111 |
sprintf(
|
112 |
/* translators: %1$s is a PHP class name. */
|
@@ -190,7 +195,12 @@ class Config {
|
|
190 |
* Enables the JITM feature.
|
191 |
*/
|
192 |
protected function enable_jitm() {
|
193 |
-
JITM
|
|
|
|
|
|
|
|
|
|
|
194 |
|
195 |
return true;
|
196 |
}
|
8 |
namespace Automattic\Jetpack;
|
9 |
|
10 |
use Automattic\Jetpack\Connection\Manager;
|
11 |
+
use Automattic\Jetpack\JITMS\JITM as JITMS_JITM;
|
12 |
+
use Automattic\Jetpack\JITM as JITM;
|
13 |
use Automattic\Jetpack\Connection\Plugin;
|
14 |
use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
|
15 |
use Automattic\Jetpack\Sync\Main as Sync_Main;
|
92 |
}
|
93 |
|
94 |
if ( $this->config['jitm'] ) {
|
95 |
+
// Check for the JITM class in both namespaces. The namespace was changed in jetpack-jitm v1.6.
|
96 |
+
( $this->ensure_class( 'Automattic\Jetpack\JITMS\JITM', false )
|
97 |
+
|| $this->ensure_class( 'Automattic\Jetpack\JITM' ) )
|
98 |
+
&& $this->ensure_feature( 'jitm' );
|
99 |
}
|
100 |
}
|
101 |
|
103 |
* Returns true if the required class is available and alerts the user if it's not available
|
104 |
* in case the site is in debug mode.
|
105 |
*
|
106 |
+
* @param String $classname a fully qualified class name.
|
107 |
+
* @param Boolean $log_notice whether the E_USER_NOTICE should be generated if the class is not found.
|
108 |
+
*
|
109 |
* @return Boolean whether the class is available.
|
110 |
*/
|
111 |
+
protected function ensure_class( $classname, $log_notice = true ) {
|
112 |
$available = class_exists( $classname );
|
113 |
|
114 |
+
if ( $log_notice && ! $available && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
115 |
trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
|
116 |
sprintf(
|
117 |
/* translators: %1$s is a PHP class name. */
|
195 |
* Enables the JITM feature.
|
196 |
*/
|
197 |
protected function enable_jitm() {
|
198 |
+
if ( class_exists( 'Automattic\Jetpack\JITMS\JITM' ) ) {
|
199 |
+
JITMS_JITM::configure();
|
200 |
+
} else {
|
201 |
+
// Provides compatibility with jetpack-jitm <v1.6.
|
202 |
+
JITM::configure();
|
203 |
+
}
|
204 |
|
205 |
return true;
|
206 |
}
|
vendor/automattic/jetpack-connection/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
wordpress
|
vendor/automattic/jetpack-connection/composer.json
CHANGED
@@ -4,13 +4,15 @@
|
|
4 |
"type": "library",
|
5 |
"license": "GPL-2.0-or-later",
|
6 |
"require": {
|
7 |
-
"automattic/jetpack-constants": "1.
|
8 |
-
"automattic/jetpack-options": "1.
|
9 |
-
"automattic/jetpack-roles": "1.0
|
|
|
10 |
},
|
11 |
"require-dev": {
|
12 |
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5",
|
13 |
-
"php-mock/php-mock": "^2.1"
|
|
|
14 |
},
|
15 |
"autoload": {
|
16 |
"files": [
|
@@ -25,7 +27,8 @@
|
|
25 |
"phpunit": [
|
26 |
"@composer install",
|
27 |
"./vendor/phpunit/phpunit/phpunit --colors=always"
|
28 |
-
]
|
|
|
29 |
},
|
30 |
"repositories": [
|
31 |
{
|
4 |
"type": "library",
|
5 |
"license": "GPL-2.0-or-later",
|
6 |
"require": {
|
7 |
+
"automattic/jetpack-constants": "1.4.0",
|
8 |
+
"automattic/jetpack-options": "1.7.0",
|
9 |
+
"automattic/jetpack-roles": "1.2.0",
|
10 |
+
"automattic/jetpack-status": "1.3.0"
|
11 |
},
|
12 |
"require-dev": {
|
13 |
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5",
|
14 |
+
"php-mock/php-mock": "^2.1",
|
15 |
+
"automattic/wordbless": "@dev"
|
16 |
},
|
17 |
"autoload": {
|
18 |
"files": [
|
27 |
"phpunit": [
|
28 |
"@composer install",
|
29 |
"./vendor/phpunit/phpunit/phpunit --colors=always"
|
30 |
+
],
|
31 |
+
"post-update-cmd": "php -r \"copy('vendor/automattic/wordbless/src/dbless-wpdb.php', 'wordpress/wp-content/db.php');\""
|
32 |
},
|
33 |
"repositories": [
|
34 |
{
|
vendor/automattic/jetpack-connection/docs/register-site.md
CHANGED
@@ -69,7 +69,15 @@ add_action( 'admin_post_register_site', 'your_plugin_register_site' );
|
|
69 |
|
70 |
function your_plugin_register_site() {
|
71 |
check_admin_referer( 'register-site' );
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
// This is where you could put your error handling, redirects, or whatever decorations you need.
|
75 |
}
|
@@ -101,12 +109,91 @@ use Automattic\Jetpack\Connection\Manager;
|
|
101 |
function your_plugin_disconnect_site() {
|
102 |
check_admin_referer( 'disconnect-site' );
|
103 |
|
104 |
-
|
105 |
-
( new Manager( 'plugin-slug' ) )->disconnect_site_wpcom();
|
106 |
|
107 |
-
//
|
108 |
-
|
|
|
|
|
109 |
|
110 |
// Your error handling and decorations
|
111 |
}
|
112 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
|
70 |
function your_plugin_register_site() {
|
71 |
check_admin_referer( 'register-site' );
|
72 |
+
$manager = new Manager( 'plugin-slug' );
|
73 |
+
|
74 |
+
// Mark the plugin connection as enabled, in case it was disabled earlier.
|
75 |
+
$manager->enable_plugin();
|
76 |
+
|
77 |
+
// If the token doesn't exist (see "Soft and Hard Disconnect" section below), we need to register the site.
|
78 |
+
if ( ! $manager->get_access_token() ) {
|
79 |
+
$manager->register();
|
80 |
+
}
|
81 |
|
82 |
// This is where you could put your error handling, redirects, or whatever decorations you need.
|
83 |
}
|
109 |
function your_plugin_disconnect_site() {
|
110 |
check_admin_referer( 'disconnect-site' );
|
111 |
|
112 |
+
$manager = new Manager( 'plugin-slug' );
|
|
|
113 |
|
114 |
+
// Mark the plugin connection as disabled.
|
115 |
+
// If there are no other plugins using the connection, destroy blog and user tokens,
|
116 |
+
// as well as the tokens stored in wordpress.com
|
117 |
+
$manager->remove_connection();
|
118 |
|
119 |
// Your error handling and decorations
|
120 |
}
|
121 |
```
|
122 |
+
|
123 |
+
|
124 |
+
#### Custom Disconnect
|
125 |
+
|
126 |
+
Method `$manager->remove_connection()` essentially calls `disable_plugin()`, `disconnect_site_wpcom()`, and `delete_all_connection_tokens()`.
|
127 |
+
Its purpose is to simplify the disconnection process.
|
128 |
+
If you need to customize the disconnect process, or perform a partial disconnect, you can call these methods one by one.
|
129 |
+
|
130 |
+
```php
|
131 |
+
// Mark the plugin connection as disabled.
|
132 |
+
|
133 |
+
$manager = new Manager( 'plugin-slug' );
|
134 |
+
|
135 |
+
// Mark the plugin connection as disabled.
|
136 |
+
$manager->disable_plugin();
|
137 |
+
|
138 |
+
// If no other plugins use the connection, this will destroy the blog tokens on both this site, and the tokens stored on wordpress.com
|
139 |
+
$manager->disconnect_site_wpcom();
|
140 |
+
|
141 |
+
// If no other plugins use the connection, this will clear all the tokens!
|
142 |
+
$manager->delete_all_connection_tokens();
|
143 |
+
```
|
144 |
+
|
145 |
+
### Soft and Hard Disconnect
|
146 |
+
|
147 |
+
There are two types of disconnection happening when you request a disconnect: *Soft Disconnect* and *Hard Disconnect*.
|
148 |
+
The package API takes care of that under the hood, so in most cases there won't be any need to differentiate them in your code.
|
149 |
+
Below is some basic information on how they differ.
|
150 |
+
|
151 |
+
#### Soft Disconnect
|
152 |
+
|
153 |
+
Soft disconnect means that all the tokens are preserved, and the connection for other plugins stays active.
|
154 |
+
Technically speaking, soft disconnect happens when you call `$manager->disable_plugin();`.
|
155 |
+
Next time you try to use the connection, you call `$manager->is_plugin_enabled()`, which will return `false`.
|
156 |
+
|
157 |
+
Calling `$manager->disconnect_site_wpcom()` and `$manager->delete_all_connection_tokens()` afterwards is still needed.
|
158 |
+
These calls will determine if the aforementioned plugin is the only one using the connection, and perform *soft* or *hard* disconnect accordingly.
|
159 |
+
|
160 |
+
#### Hard Disconnect
|
161 |
+
|
162 |
+
If there are no other plugins using the connection, or all of them have already been *softly disconnected*, the package will perform the *hard disconnect*.
|
163 |
+
In that case methods `disconnect_site_wpcom()` and `delete_all_connection_tokens()` will actually remove the tokens and run the `deregister` API request.
|
164 |
+
|
165 |
+
You can explicitly request hard disconnect by providing the argument `$ignore_connected_plugins`:
|
166 |
+
```php
|
167 |
+
$manager = new Manager( 'plugin-slug' );
|
168 |
+
|
169 |
+
$manager->disable_plugin();
|
170 |
+
|
171 |
+
// The `$ignore_connected_plugins` argument is set to `true`, so the Connection Manager will perform the hard disconnect.
|
172 |
+
$manager->disconnect_site_wpcom( true );
|
173 |
+
$manager->delete_all_connection_tokens( true );
|
174 |
+
```
|
175 |
+
|
176 |
+
#### Using the connection
|
177 |
+
If the plugin was *softly* disconnected, the access tokens will still be accessible.
|
178 |
+
However, the user explicitly requested the plugin to be disabled, so you need to check for that before you utilize the connection in any way:
|
179 |
+
```php
|
180 |
+
$manager = new Manager( 'plugin-slug' );
|
181 |
+
|
182 |
+
if ( $manager->is_plugin_enabled() && $manager->get_access_token() ) {
|
183 |
+
// Perform the API requests.
|
184 |
+
} else {
|
185 |
+
// Assume the plugin is disconnected, no matter if the tokens actually exist.
|
186 |
+
}
|
187 |
+
```
|
188 |
+
|
189 |
+
#### Reconnecting
|
190 |
+
Whether a *soft* or *hard* disconnect was performed, the plugin will be marked as "disconnected", so if the connect is being reestablished, you need to call `$manager->enable_plugin()` to remove that flag.
|
191 |
+
If the plugin was *softly* disconnected, removing the flag is enough for it to work. Otherwise, you'll need to register the website again:
|
192 |
+
```php
|
193 |
+
$manager = new Manager( 'plugin-slug' );
|
194 |
+
$manager->enable_plugin();
|
195 |
+
|
196 |
+
if ( ! $manager->get_access_token() ) {
|
197 |
+
$manager->register();
|
198 |
+
}
|
199 |
+
```
|
vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php
CHANGED
@@ -49,10 +49,11 @@ class Jetpack_IXR_Client extends IXR_Client {
|
|
49 |
/**
|
50 |
* Perform the IXR request.
|
51 |
*
|
|
|
|
|
52 |
* @return bool True if request succeeded, false otherwise.
|
53 |
*/
|
54 |
-
public function query() {
|
55 |
-
$args = func_get_args();
|
56 |
$method = array_shift( $args );
|
57 |
$request = new IXR_Request( $method, $args );
|
58 |
$xml = trim( $request->getXml() );
|
49 |
/**
|
50 |
* Perform the IXR request.
|
51 |
*
|
52 |
+
* @param string[] ...$args IXR args.
|
53 |
+
*
|
54 |
* @return bool True if request succeeded, false otherwise.
|
55 |
*/
|
56 |
+
public function query( ...$args ) {
|
|
|
57 |
$method = array_shift( $args );
|
58 |
$request = new IXR_Request( $method, $args );
|
59 |
$xml = trim( $request->getXml() );
|
vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php
CHANGED
@@ -23,9 +23,10 @@ class Jetpack_IXR_ClientMulticall extends Jetpack_IXR_Client {
|
|
23 |
* Add a IXR call to the client.
|
24 |
* First argument is the method name.
|
25 |
* The rest of the arguments are the params specified to the method.
|
|
|
|
|
26 |
*/
|
27 |
-
public function addCall() {
|
28 |
-
$args = func_get_args();
|
29 |
$method_name = array_shift( $args );
|
30 |
$struct = array(
|
31 |
'methodName' => $method_name,
|
@@ -37,9 +38,11 @@ class Jetpack_IXR_ClientMulticall extends Jetpack_IXR_Client {
|
|
37 |
/**
|
38 |
* Perform the IXR multicall request.
|
39 |
*
|
|
|
|
|
40 |
* @return bool True if request succeeded, false otherwise.
|
41 |
*/
|
42 |
-
public function query() {
|
43 |
usort( $this->calls, array( $this, 'sort_calls' ) );
|
44 |
|
45 |
// Prepare multicall, then call the parent::query() method.
|
23 |
* Add a IXR call to the client.
|
24 |
* First argument is the method name.
|
25 |
* The rest of the arguments are the params specified to the method.
|
26 |
+
*
|
27 |
+
* @param string[] ...$args IXR args.
|
28 |
*/
|
29 |
+
public function addCall( ...$args ) {
|
|
|
30 |
$method_name = array_shift( $args );
|
31 |
$struct = array(
|
32 |
'methodName' => $method_name,
|
38 |
/**
|
39 |
* Perform the IXR multicall request.
|
40 |
*
|
41 |
+
* @param string[] ...$args IXR args.
|
42 |
+
*
|
43 |
* @return bool True if request succeeded, false otherwise.
|
44 |
*/
|
45 |
+
public function query( ...$args ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
46 |
usort( $this->calls, array( $this, 'sort_calls' ) );
|
47 |
|
48 |
// Prepare multicall, then call the parent::query() method.
|
vendor/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php
CHANGED
@@ -56,29 +56,35 @@ class Jetpack_XMLRPC_Server {
|
|
56 |
*/
|
57 |
public function xmlrpc_methods( $core_methods ) {
|
58 |
$jetpack_methods = array(
|
59 |
-
'jetpack.jsonAPI' => array( $this, 'json_api' ),
|
60 |
'jetpack.verifyAction' => array( $this, 'verify_action' ),
|
61 |
'jetpack.getUser' => array( $this, 'get_user' ),
|
62 |
'jetpack.remoteRegister' => array( $this, 'remote_register' ),
|
63 |
'jetpack.remoteProvision' => array( $this, 'remote_provision' ),
|
64 |
);
|
65 |
|
|
|
|
|
|
|
|
|
66 |
$this->user = $this->login();
|
67 |
|
68 |
if ( $this->user ) {
|
69 |
$jetpack_methods = array_merge(
|
70 |
$jetpack_methods,
|
71 |
array(
|
72 |
-
'jetpack.
|
73 |
-
'jetpack.
|
74 |
-
'jetpack.
|
75 |
-
'jetpack.
|
76 |
-
'jetpack.disconnectBlog' => array( $this, 'disconnect_blog' ),
|
77 |
-
'jetpack.unlinkUser' => array( $this, 'unlink_user' ),
|
78 |
-
'jetpack.idcUrlValidation' => array( $this, 'validate_urls_for_idc_mitigation' ),
|
79 |
)
|
80 |
);
|
81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
if ( isset( $core_methods['metaWeblog.editPost'] ) ) {
|
83 |
$jetpack_methods['metaWeblog.newMediaObject'] = $core_methods['metaWeblog.newMediaObject'];
|
84 |
$jetpack_methods['jetpack.updateAttachmentParent'] = array( $this, 'update_attachment_parent' );
|
@@ -159,7 +165,7 @@ class Jetpack_XMLRPC_Server {
|
|
159 |
|
160 |
if ( ! $user_id ) {
|
161 |
return $this->error(
|
162 |
-
new
|
163 |
'invalid_user',
|
164 |
__( 'Invalid user identifier.', 'jetpack' ),
|
165 |
400
|
@@ -172,7 +178,7 @@ class Jetpack_XMLRPC_Server {
|
|
172 |
|
173 |
if ( ! $user ) {
|
174 |
return $this->error(
|
175 |
-
new
|
176 |
'user_unknown',
|
177 |
__( 'User not found.', 'jetpack' ),
|
178 |
404
|
@@ -230,18 +236,18 @@ class Jetpack_XMLRPC_Server {
|
|
230 |
foreach ( array( 'secret', 'state', 'redirect_uri', 'code' ) as $required ) {
|
231 |
if ( ! isset( $request[ $required ] ) || empty( $request[ $required ] ) ) {
|
232 |
return $this->error(
|
233 |
-
new
|
234 |
'remote_authorize'
|
235 |
);
|
236 |
}
|
237 |
}
|
238 |
|
239 |
if ( ! $user ) {
|
240 |
-
return $this->error( new
|
241 |
}
|
242 |
|
243 |
if ( $this->connection->is_active() && $this->connection->is_user_connected( $request['state'] ) ) {
|
244 |
-
return $this->error( new
|
245 |
}
|
246 |
|
247 |
$verified = $this->verify_action( array( 'authorize', $request['secret'], $request['state'] ) );
|
@@ -293,7 +299,7 @@ class Jetpack_XMLRPC_Server {
|
|
293 |
|
294 |
if ( empty( $request['nonce'] ) ) {
|
295 |
return $this->error(
|
296 |
-
new
|
297 |
'nonce_missing',
|
298 |
__( 'The required "nonce" parameter is missing.', 'jetpack' ),
|
299 |
400
|
@@ -319,7 +325,7 @@ class Jetpack_XMLRPC_Server {
|
|
319 |
'OK' !== trim( wp_remote_retrieve_body( $response ) )
|
320 |
) {
|
321 |
return $this->error(
|
322 |
-
new
|
323 |
'invalid_nonce',
|
324 |
__( 'There was an issue validating this request.', 'jetpack' ),
|
325 |
400
|
@@ -338,7 +344,7 @@ class Jetpack_XMLRPC_Server {
|
|
338 |
return $this->error( $registered, 'remote_register' );
|
339 |
} elseif ( ! $registered ) {
|
340 |
return $this->error(
|
341 |
-
new
|
342 |
'registration_error',
|
343 |
__( 'There was an unspecified error registering the site', 'jetpack' ),
|
344 |
400
|
@@ -506,7 +512,7 @@ class Jetpack_XMLRPC_Server {
|
|
506 |
private function fetch_and_verify_local_user( $request ) {
|
507 |
if ( empty( $request['local_user'] ) ) {
|
508 |
return $this->error(
|
509 |
-
new
|
510 |
'local_user_missing',
|
511 |
__( 'The required "local_user" parameter is missing.', 'jetpack' ),
|
512 |
400
|
@@ -589,13 +595,13 @@ class Jetpack_XMLRPC_Server {
|
|
589 |
$user = wp_authenticate( 'username', 'password' );
|
590 |
if ( is_wp_error( $user ) ) {
|
591 |
if ( 'authentication_failed' === $user->get_error_code() ) { // Generic error could mean most anything.
|
592 |
-
$this->error = new
|
593 |
} else {
|
594 |
$this->error = $user;
|
595 |
}
|
596 |
return false;
|
597 |
} elseif ( ! $user ) { // Shouldn't happen.
|
598 |
-
$this->error = new
|
599 |
return false;
|
600 |
}
|
601 |
|
56 |
*/
|
57 |
public function xmlrpc_methods( $core_methods ) {
|
58 |
$jetpack_methods = array(
|
|
|
59 |
'jetpack.verifyAction' => array( $this, 'verify_action' ),
|
60 |
'jetpack.getUser' => array( $this, 'get_user' ),
|
61 |
'jetpack.remoteRegister' => array( $this, 'remote_register' ),
|
62 |
'jetpack.remoteProvision' => array( $this, 'remote_provision' ),
|
63 |
);
|
64 |
|
65 |
+
if ( class_exists( 'Jetpack' ) ) {
|
66 |
+
$jetpack_methods['jetpack.jsonAPI'] = array( $this, 'json_api' );
|
67 |
+
}
|
68 |
+
|
69 |
$this->user = $this->login();
|
70 |
|
71 |
if ( $this->user ) {
|
72 |
$jetpack_methods = array_merge(
|
73 |
$jetpack_methods,
|
74 |
array(
|
75 |
+
'jetpack.testAPIUserCode' => array( $this, 'test_api_user_code' ),
|
76 |
+
'jetpack.disconnectBlog' => array( $this, 'disconnect_blog' ),
|
77 |
+
'jetpack.unlinkUser' => array( $this, 'unlink_user' ),
|
78 |
+
'jetpack.idcUrlValidation' => array( $this, 'validate_urls_for_idc_mitigation' ),
|
|
|
|
|
|
|
79 |
)
|
80 |
);
|
81 |
|
82 |
+
if ( class_exists( 'Jetpack' ) ) {
|
83 |
+
$jetpack_methods['jetpack.testConnection'] = array( $this, 'test_connection' );
|
84 |
+
$jetpack_methods['jetpack.featuresAvailable'] = array( $this, 'features_available' );
|
85 |
+
$jetpack_methods['jetpack.featuresEnabled'] = array( $this, 'features_enabled' );
|
86 |
+
}
|
87 |
+
|
88 |
if ( isset( $core_methods['metaWeblog.editPost'] ) ) {
|
89 |
$jetpack_methods['metaWeblog.newMediaObject'] = $core_methods['metaWeblog.newMediaObject'];
|
90 |
$jetpack_methods['jetpack.updateAttachmentParent'] = array( $this, 'update_attachment_parent' );
|
165 |
|
166 |
if ( ! $user_id ) {
|
167 |
return $this->error(
|
168 |
+
new \WP_Error(
|
169 |
'invalid_user',
|
170 |
__( 'Invalid user identifier.', 'jetpack' ),
|
171 |
400
|
178 |
|
179 |
if ( ! $user ) {
|
180 |
return $this->error(
|
181 |
+
new \WP_Error(
|
182 |
'user_unknown',
|
183 |
__( 'User not found.', 'jetpack' ),
|
184 |
404
|
236 |
foreach ( array( 'secret', 'state', 'redirect_uri', 'code' ) as $required ) {
|
237 |
if ( ! isset( $request[ $required ] ) || empty( $request[ $required ] ) ) {
|
238 |
return $this->error(
|
239 |
+
new \WP_Error( 'missing_parameter', 'One or more parameters is missing from the request.', 400 ),
|
240 |
'remote_authorize'
|
241 |
);
|
242 |
}
|
243 |
}
|
244 |
|
245 |
if ( ! $user ) {
|
246 |
+
return $this->error( new \WP_Error( 'user_unknown', 'User not found.', 404 ), 'remote_authorize' );
|
247 |
}
|
248 |
|
249 |
if ( $this->connection->is_active() && $this->connection->is_user_connected( $request['state'] ) ) {
|
250 |
+
return $this->error( new \WP_Error( 'already_connected', 'User already connected.', 400 ), 'remote_authorize' );
|
251 |
}
|
252 |
|
253 |
$verified = $this->verify_action( array( 'authorize', $request['secret'], $request['state'] ) );
|
299 |
|
300 |
if ( empty( $request['nonce'] ) ) {
|
301 |
return $this->error(
|
302 |
+
new \WP_Error(
|
303 |
'nonce_missing',
|
304 |
__( 'The required "nonce" parameter is missing.', 'jetpack' ),
|
305 |
400
|
325 |
'OK' !== trim( wp_remote_retrieve_body( $response ) )
|
326 |
) {
|
327 |
return $this->error(
|
328 |
+
new \WP_Error(
|
329 |
'invalid_nonce',
|
330 |
__( 'There was an issue validating this request.', 'jetpack' ),
|
331 |
400
|
344 |
return $this->error( $registered, 'remote_register' );
|
345 |
} elseif ( ! $registered ) {
|
346 |
return $this->error(
|
347 |
+
new \WP_Error(
|
348 |
'registration_error',
|
349 |
__( 'There was an unspecified error registering the site', 'jetpack' ),
|
350 |
400
|
512 |
private function fetch_and_verify_local_user( $request ) {
|
513 |
if ( empty( $request['local_user'] ) ) {
|
514 |
return $this->error(
|
515 |
+
new \WP_Error(
|
516 |
'local_user_missing',
|
517 |
__( 'The required "local_user" parameter is missing.', 'jetpack' ),
|
518 |
400
|
595 |
$user = wp_authenticate( 'username', 'password' );
|
596 |
if ( is_wp_error( $user ) ) {
|
597 |
if ( 'authentication_failed' === $user->get_error_code() ) { // Generic error could mean most anything.
|
598 |
+
$this->error = new \WP_Error( 'invalid_request', 'Invalid Request', 403 );
|
599 |
} else {
|
600 |
$this->error = $user;
|
601 |
}
|
602 |
return false;
|
603 |
} elseif ( ! $user ) { // Shouldn't happen.
|
604 |
+
$this->error = new \WP_Error( 'invalid_request', 'Invalid Request', 403 );
|
605 |
return false;
|
606 |
}
|
607 |
|
vendor/automattic/jetpack-connection/src/class-error-handler.php
ADDED
@@ -0,0 +1,621 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The Jetpack Connection error class file.
|
4 |
+
*
|
5 |
+
* @package automattic/jetpack-connection
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Automattic\Jetpack\Connection;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* The Jetpack Connection Errors that handles errors
|
12 |
+
*
|
13 |
+
* This class handles the following workflow:
|
14 |
+
*
|
15 |
+
* 1. A XML-RCP request with an invalid signature triggers a error
|
16 |
+
* 2. Applies a gate to only process each error code once an hour to avoid overflow
|
17 |
+
* 3. It stores the error on the database, but we don't know yet if this is a valid error, because
|
18 |
+
* we can't confirm it came from WP.com.
|
19 |
+
* 4. It encrypts the error details and send it to thw wp.com server
|
20 |
+
* 5. wp.com checks it and, if valid, sends a new request back to this site using the verify_xml_rpc_error REST endpoint
|
21 |
+
* 6. This endpoint add this error to the Verified errors in the database
|
22 |
+
* 7. Triggers a workflow depending on the error (display user an error message, do some self healing, etc.)
|
23 |
+
*
|
24 |
+
* Errors are stored in the database as options in the following format:
|
25 |
+
*
|
26 |
+
* [
|
27 |
+
* $error_code => [
|
28 |
+
* $user_id => [
|
29 |
+
* $error_details
|
30 |
+
* ]
|
31 |
+
* ]
|
32 |
+
* ]
|
33 |
+
*
|
34 |
+
* For each error code we store a maximum of 5 errors for 5 different user ids.
|
35 |
+
*
|
36 |
+
* An user ID can be
|
37 |
+
* * 0 for blog tokens
|
38 |
+
* * positive integer for user tokens
|
39 |
+
* * 'invalid' for malformed tokens
|
40 |
+
*
|
41 |
+
* @since 8.7.0
|
42 |
+
*/
|
43 |
+
class Error_Handler {
|
44 |
+
|
45 |
+
/**
|
46 |
+
* The name of the option that stores the errors
|
47 |
+
*
|
48 |
+
* @since 8.7.0
|
49 |
+
*
|
50 |
+
* @var string
|
51 |
+
*/
|
52 |
+
const STORED_ERRORS_OPTION = 'jetpack_connection_xmlrpc_errors';
|
53 |
+
|
54 |
+
/**
|
55 |
+
* The name of the option that stores the errors
|
56 |
+
*
|
57 |
+
* @since 8.7.0
|
58 |
+
*
|
59 |
+
* @var string
|
60 |
+
*/
|
61 |
+
const STORED_VERIFIED_ERRORS_OPTION = 'jetpack_connection_xmlrpc_verified_errors';
|
62 |
+
|
63 |
+
/**
|
64 |
+
* The prefix of the transient that controls the gate for each error code
|
65 |
+
*
|
66 |
+
* @since 8.7.0
|
67 |
+
*
|
68 |
+
* @var string
|
69 |
+
*/
|
70 |
+
const ERROR_REPORTING_GATE = 'jetpack_connection_error_reporting_gate_';
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Time in seconds a test should live in the database before being discarded
|
74 |
+
*
|
75 |
+
* @since 8.7.0
|
76 |
+
*/
|
77 |
+
const ERROR_LIFE_TIME = DAY_IN_SECONDS;
|
78 |
+
/**
|
79 |
+
* List of known errors. Only error codes in this list will be handled
|
80 |
+
*
|
81 |
+
* @since 8.7.0
|
82 |
+
*
|
83 |
+
* @var array
|
84 |
+
*/
|
85 |
+
public $known_errors = array(
|
86 |
+
'malformed_token',
|
87 |
+
'malformed_user_id',
|
88 |
+
'unknown_user',
|
89 |
+
'no_user_tokens',
|
90 |
+
'empty_master_user_option',
|
91 |
+
'no_token_for_user',
|
92 |
+
'token_malformed',
|
93 |
+
'user_id_mismatch',
|
94 |
+
'no_possible_tokens',
|
95 |
+
'no_valid_token',
|
96 |
+
'unknown_token',
|
97 |
+
'could_not_sign',
|
98 |
+
'invalid_scheme',
|
99 |
+
'invalid_secret',
|
100 |
+
'invalid_token',
|
101 |
+
'token_mismatch',
|
102 |
+
'invalid_body',
|
103 |
+
'invalid_signature',
|
104 |
+
'invalid_body_hash',
|
105 |
+
'invalid_nonce',
|
106 |
+
'signature_mismatch',
|
107 |
+
);
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Holds the instance of this singleton class
|
111 |
+
*
|
112 |
+
* @since 8.7.0
|
113 |
+
*
|
114 |
+
* @var Error_Handler $instance
|
115 |
+
*/
|
116 |
+
public static $instance = null;
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Initialize instance, hookds and load verified errors handlers
|
120 |
+
*
|
121 |
+
* @since 8.7.0
|
122 |
+
*/
|
123 |
+
private function __construct() {
|
124 |
+
defined( 'JETPACK__ERRORS_PUBLIC_KEY' ) || define( 'JETPACK__ERRORS_PUBLIC_KEY', 'KdZY80axKX+nWzfrOcizf0jqiFHnrWCl9X8yuaClKgM=' );
|
125 |
+
|
126 |
+
add_action( 'rest_api_init', array( $this, 'register_verify_error_endpoint' ) );
|
127 |
+
|
128 |
+
$this->handle_verified_errors();
|
129 |
+
|
130 |
+
// If the site gets reconnected, clear errors.
|
131 |
+
add_action( 'jetpack_site_registered', array( $this, 'delete_all_errors' ) );
|
132 |
+
add_action( 'jetpack_get_site_data_success', array( $this, 'delete_all_errors' ) );
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Gets the list of verified errors and act upon them
|
137 |
+
*
|
138 |
+
* @since 8.7.0
|
139 |
+
*
|
140 |
+
* @return void
|
141 |
+
*/
|
142 |
+
public function handle_verified_errors() {
|
143 |
+
$verified_errors = $this->get_verified_errors();
|
144 |
+
foreach ( $verified_errors as $error_code => $user_errors ) {
|
145 |
+
|
146 |
+
switch ( $error_code ) {
|
147 |
+
case 'malformed_token':
|
148 |
+
case 'token_malformed':
|
149 |
+
case 'no_possible_tokens':
|
150 |
+
case 'no_valid_token':
|
151 |
+
case 'unknown_token':
|
152 |
+
case 'could_not_sign':
|
153 |
+
case 'invalid_token':
|
154 |
+
case 'token_mismatch':
|
155 |
+
case 'invalid_signature':
|
156 |
+
case 'signature_mismatch':
|
157 |
+
new Error_Handlers\Invalid_Blog_Token( $user_errors );
|
158 |
+
break;
|
159 |
+
}
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Gets the instance of this singleton class
|
165 |
+
*
|
166 |
+
* @since 8.7.0
|
167 |
+
*
|
168 |
+
* @return Error_Handler $instance
|
169 |
+
*/
|
170 |
+
public static function get_instance() {
|
171 |
+
if ( is_null( self::$instance ) ) {
|
172 |
+
self::$instance = new self();
|
173 |
+
}
|
174 |
+
return self::$instance;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Keep track of a connection error that was encountered
|
179 |
+
*
|
180 |
+
* @since 8.7.0
|
181 |
+
*
|
182 |
+
* @param \WP_Error $error the error object.
|
183 |
+
* @param boolean $force Force the report, even if should_report_error is false.
|
184 |
+
* @return void
|
185 |
+
*/
|
186 |
+
public function report_error( \WP_Error $error, $force = false ) {
|
187 |
+
if ( in_array( $error->get_error_code(), $this->known_errors, true ) && $this->should_report_error( $error ) || $force ) {
|
188 |
+
$stored_error = $this->store_error( $error );
|
189 |
+
if ( $stored_error ) {
|
190 |
+
$this->send_error_to_wpcom( $stored_error );
|
191 |
+
}
|
192 |
+
}
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Checks the status of the gate
|
197 |
+
*
|
198 |
+
* This protects the site (and WPCOM) against over loads.
|
199 |
+
*
|
200 |
+
* @since 8.7.0
|
201 |
+
*
|
202 |
+
* @param \WP_Error $error the error object.
|
203 |
+
* @return boolean $should_report True if gate is open and the error should be reported.
|
204 |
+
*/
|
205 |
+
public function should_report_error( \WP_Error $error ) {
|
206 |
+
|
207 |
+
if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
|
208 |
+
return true;
|
209 |
+
}
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Whether to bypass the gate for XML-RPC error handling
|
213 |
+
*
|
214 |
+
* By default, we only process XML-RPC errors once an hour for each error code.
|
215 |
+
* This is done to avoid overflows. If you need to disable this gate, you can set this variable to true.
|
216 |
+
*
|
217 |
+
* This filter is useful for unit testing
|
218 |
+
*
|
219 |
+
* @since 8.7.0
|
220 |
+
*
|
221 |
+
* @param boolean $bypass_gate whether to bypass the gate. Default is false, do not bypass.
|
222 |
+
*/
|
223 |
+
$bypass_gate = apply_filters( 'jetpack_connection_bypass_error_reporting_gate', false );
|
224 |
+
if ( true === $bypass_gate ) {
|
225 |
+
return true;
|
226 |
+
}
|
227 |
+
|
228 |
+
$transient = self::ERROR_REPORTING_GATE . $error->get_error_code();
|
229 |
+
|
230 |
+
if ( get_transient( $transient ) ) {
|
231 |
+
return false;
|
232 |
+
}
|
233 |
+
|
234 |
+
set_transient( $transient, true, HOUR_IN_SECONDS );
|
235 |
+
return true;
|
236 |
+
}
|
237 |
+
|
238 |
+
/**
|
239 |
+
* Stores the error in the database so we know there is an issue and can inform the user
|
240 |
+
*
|
241 |
+
* @since 8.7.0
|
242 |
+
*
|
243 |
+
* @param \WP_Error $error the error object.
|
244 |
+
* @return boolean|array False if stored errors were not updated and the error array if it was successfully stored.
|
245 |
+
*/
|
246 |
+
public function store_error( \WP_Error $error ) {
|
247 |
+
|
248 |
+
$stored_errors = $this->get_stored_errors();
|
249 |
+
$error_array = $this->wp_error_to_array( $error );
|
250 |
+
$error_code = $error->get_error_code();
|
251 |
+
$user_id = $error_array['user_id'];
|
252 |
+
|
253 |
+
if ( ! isset( $stored_errors[ $error_code ] ) || ! is_array( $stored_errors[ $error_code ] ) ) {
|
254 |
+
$stored_errors[ $error_code ] = array();
|
255 |
+
}
|
256 |
+
|
257 |
+
$stored_errors[ $error_code ][ $user_id ] = $error_array;
|
258 |
+
|
259 |
+
// Let's store a maximum of 5 different user ids for each error code.
|
260 |
+
if ( count( $stored_errors[ $error_code ] ) > 5 ) {
|
261 |
+
// array_shift will destroy keys here because they are numeric, so manually remove first item.
|
262 |
+
$keys = array_keys( $stored_errors[ $error_code ] );
|
263 |
+
unset( $stored_errors[ $error_code ][ $keys[0] ] );
|
264 |
+
}
|
265 |
+
|
266 |
+
if ( update_option( self::STORED_ERRORS_OPTION, $stored_errors ) ) {
|
267 |
+
return $error_array;
|
268 |
+
}
|
269 |
+
|
270 |
+
return false;
|
271 |
+
|
272 |
+
}
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Converts a WP_Error object in the array representation we store in the database
|
276 |
+
*
|
277 |
+
* @since 8.7.0
|
278 |
+
*
|
279 |
+
* @param \WP_Error $error the error object.
|
280 |
+
* @return boolean|array False if error is invalid or the error array
|
281 |
+
*/
|
282 |
+
public function wp_error_to_array( \WP_Error $error ) {
|
283 |
+
|
284 |
+
$data = $error->get_error_data();
|
285 |
+
|
286 |
+
if ( ! isset( $data['signature_details'] ) || ! is_array( $data['signature_details'] ) ) {
|
287 |
+
return false;
|
288 |
+
}
|
289 |
+
|
290 |
+
$data = $data['signature_details'];
|
291 |
+
|
292 |
+
if ( ! isset( $data['token'] ) || empty( $data['token'] ) ) {
|
293 |
+
return false;
|
294 |
+
}
|
295 |
+
|
296 |
+
$user_id = $this->get_user_id_from_token( $data['token'] );
|
297 |
+
|
298 |
+
$error_array = array(
|
299 |
+
'error_code' => $error->get_error_code(),
|
300 |
+
'user_id' => $user_id,
|
301 |
+
'error_message' => $error->get_error_message(),
|
302 |
+
'error_data' => $data,
|
303 |
+
'timestamp' => time(),
|
304 |
+
'nonce' => wp_generate_password( 10, false ),
|
305 |
+
);
|
306 |
+
|
307 |
+
if ( $this->track_lost_active_master_user( $error->get_error_code(), $data['token'], $user_id ) ) {
|
308 |
+
$error_array['error_message'] = 'Site has a deleted but active master user token';
|
309 |
+
}
|
310 |
+
|
311 |
+
return $error_array;
|
312 |
+
|
313 |
+
}
|
314 |
+
|
315 |
+
/**
|
316 |
+
* This is been used to track blogs with deleted master user but whose tokens are still actively being used
|
317 |
+
*
|
318 |
+
* See p9dueE-1GB-p2
|
319 |
+
*
|
320 |
+
* This tracking should be removed as long as we no longer need, possibly in 8.9
|
321 |
+
*
|
322 |
+
* @since 8.8.1
|
323 |
+
*
|
324 |
+
* @param string $error_code The error code.
|
325 |
+
* @param string $token The token that triggered the error.
|
326 |
+
* @param integer $user_id The user ID used to make the request that triggered the error.
|
327 |
+
* @return boolean
|
328 |
+
*/
|
329 |
+
private function track_lost_active_master_user( $error_code, $token, $user_id ) {
|
330 |
+
if ( 'unknown_user' === $error_code ) {
|
331 |
+
$manager = new Manager();
|
332 |
+
// If the Unknown user is the master user (master user has been deleted).
|
333 |
+
if ( $manager->is_missing_connection_owner() && (int) $user_id === (int) $manager->get_connection_owner_id() ) {
|
334 |
+
$user_token = $manager->get_access_token( JETPACK_MASTER_USER );
|
335 |
+
// If there's still a token stored for the deleted master user.
|
336 |
+
if ( $user_token && is_object( $user_token ) && isset( $user_token->secret ) ) {
|
337 |
+
$token_parts = explode( ':', wp_unslash( $token ) );
|
338 |
+
// If the token stored for the deleted master user matches the token user by wpcom to make the request.
|
339 |
+
// This means that requests FROM this site TO wpcom using the JETPACK_MASTER_USER constant are still working.
|
340 |
+
if ( isset( $token_parts[0] ) && ! empty( $token_parts[0] ) && false !== strpos( $user_token->secret, $token_parts[0] ) ) {
|
341 |
+
return true;
|
342 |
+
}
|
343 |
+
}
|
344 |
+
}
|
345 |
+
}
|
346 |
+
return false;
|
347 |
+
}
|
348 |
+
|
349 |
+
/**
|
350 |
+
* Sends the error to WP.com to be verified
|
351 |
+
*
|
352 |
+
* @since 8.7.0
|
353 |
+
*
|
354 |
+
* @param array $error_array The array representation of the error as it is stored in the database.
|
355 |
+
* @return bool
|
356 |
+
*/
|
357 |
+
public function send_error_to_wpcom( $error_array ) {
|
358 |
+
|
359 |
+
$blog_id = \Jetpack_Options::get_option( 'id' );
|
360 |
+
|
361 |
+
$encrypted_data = $this->encrypt_data_to_wpcom( $error_array );
|
362 |
+
|
363 |
+
if ( false === $encrypted_data ) {
|
364 |
+
return false;
|
365 |
+
}
|
366 |
+
|
367 |
+
$args = array(
|
368 |
+
'body' => array(
|
369 |
+
'error_data' => $encrypted_data,
|
370 |
+
),
|
371 |
+
);
|
372 |
+
|
373 |
+
// send encrypted data to WP.com Public-API v2.
|
374 |
+
wp_remote_post( "https://public-api.wordpress.com/wpcom/v2/sites/{$blog_id}/jetpack-report-error/", $args );
|
375 |
+
return true;
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* Encrypt data to be sent over to WP.com
|
380 |
+
*
|
381 |
+
* @since 8.7.0
|
382 |
+
*
|
383 |
+
* @param array|string $data the data to be encoded.
|
384 |
+
* @return boolean|string The encoded string on success, false on failure
|
385 |
+
*/
|
386 |
+
public function encrypt_data_to_wpcom( $data ) {
|
387 |
+
|
388 |
+
try {
|
389 |
+
// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
|
390 |
+
// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
391 |
+
$encrypted_data = base64_encode( sodium_crypto_box_seal( wp_json_encode( $data ), base64_decode( JETPACK__ERRORS_PUBLIC_KEY ) ) );
|
392 |
+
// phpcs:enable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
|
393 |
+
// phpcs:enable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
|
394 |
+
} catch ( \SodiumException $e ) {
|
395 |
+
// error encrypting data.
|
396 |
+
return false;
|
397 |
+
}
|
398 |
+
|
399 |
+
return $encrypted_data;
|
400 |
+
|
401 |
+
}
|
402 |
+
|
403 |
+
/**
|
404 |
+
* Extracts the user ID from a token
|
405 |
+
*
|
406 |
+
* @since 8.7.0
|
407 |
+
*
|
408 |
+
* @param string $token the token used to make the xml-rpc request.
|
409 |
+
* @return string $the user id or `invalid` if user id not present.
|
410 |
+
*/
|
411 |
+
public function get_user_id_from_token( $token ) {
|
412 |
+
$parsed_token = explode( ':', wp_unslash( $token ) );
|
413 |
+
|
414 |
+
if ( isset( $parsed_token[2] ) && ! empty( $parsed_token[2] ) && ctype_digit( $parsed_token[2] ) ) {
|
415 |
+
$user_id = $parsed_token[2];
|
416 |
+
} else {
|
417 |
+
$user_id = 'invalid';
|
418 |
+
}
|
419 |
+
|
420 |
+
return $user_id;
|
421 |
+
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* Gets the reported errors stored in the database
|
426 |
+
*
|
427 |
+
* @since 8.7.0
|
428 |
+
*
|
429 |
+
* @return array $errors
|
430 |
+
*/
|
431 |
+
public function get_stored_errors() {
|
432 |
+
|
433 |
+
$stored_errors = get_option( self::STORED_ERRORS_OPTION );
|
434 |
+
|
435 |
+
if ( ! is_array( $stored_errors ) ) {
|
436 |
+
$stored_errors = array();
|
437 |
+
}
|
438 |
+
|
439 |
+
$stored_errors = $this->garbage_collector( $stored_errors );
|
440 |
+
|
441 |
+
return $stored_errors;
|
442 |
+
}
|
443 |
+
|
444 |
+
/**
|
445 |
+
* Gets the verified errors stored in the database
|
446 |
+
*
|
447 |
+
* @since 8.7.0
|
448 |
+
*
|
449 |
+
* @return array $errors
|
450 |
+
*/
|
451 |
+
public function get_verified_errors() {
|
452 |
+
|
453 |
+
$verified_errors = get_option( self::STORED_VERIFIED_ERRORS_OPTION );
|
454 |
+
|
455 |
+
if ( ! is_array( $verified_errors ) ) {
|
456 |
+
$verified_errors = array();
|
457 |
+
}
|
458 |
+
|
459 |
+
$verified_errors = $this->garbage_collector( $verified_errors );
|
460 |
+
|
461 |
+
return $verified_errors;
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Removes expired errors from the array
|
466 |
+
*
|
467 |
+
* This method is called by get_stored_errors and get_verified errors and filters their result
|
468 |
+
* Whenever a new error is stored to the database or verified, this will be triggered and the
|
469 |
+
* expired error will be permantently removed from the database
|
470 |
+
*
|
471 |
+
* @since 8.7.0
|
472 |
+
*
|
473 |
+
* @param array $errors array of errors as stored in the database.
|
474 |
+
* @return array
|
475 |
+
*/
|
476 |
+
private function garbage_collector( $errors ) {
|
477 |
+
foreach ( $errors as $error_code => $users ) {
|
478 |
+
foreach ( $users as $user_id => $error ) {
|
479 |
+
if ( self::ERROR_LIFE_TIME < time() - (int) $error['timestamp'] ) {
|
480 |
+
unset( $errors[ $error_code ][ $user_id ] );
|
481 |
+
}
|
482 |
+
}
|
483 |
+
}
|
484 |
+
// Clear empty error codes.
|
485 |
+
$errors = array_filter(
|
486 |
+
$errors,
|
487 |
+
function( $user_errors ) {
|
488 |
+
return ! empty( $user_errors );
|
489 |
+
}
|
490 |
+
);
|
491 |
+
return $errors;
|
492 |
+
}
|
493 |
+
|
494 |
+
/**
|
495 |
+
* Delete all stored and verified errors from the database
|
496 |
+
*
|
497 |
+
* @since 8.7.0
|
498 |
+
*
|
499 |
+
* @return void
|
500 |
+
*/
|
501 |
+
public function delete_all_errors() {
|
502 |
+
$this->delete_stored_errors();
|
503 |
+
$this->delete_verified_errors();
|
504 |
+
}
|
505 |
+
|
506 |
+
/**
|
507 |
+
* Delete the reported errors stored in the database
|
508 |
+
*
|
509 |
+
* @since 8.7.0
|
510 |
+
*
|
511 |
+
* @return boolean True, if option is successfully deleted. False on failure.
|
512 |
+
*/
|
513 |
+
public function delete_stored_errors() {
|
514 |
+
return delete_option( self::STORED_ERRORS_OPTION );
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* Delete the verified errors stored in the database
|
519 |
+
*
|
520 |
+
* @since 8.7.0
|
521 |
+
*
|
522 |
+
* @return boolean True, if option is successfully deleted. False on failure.
|
523 |
+
*/
|
524 |
+
public function delete_verified_errors() {
|
525 |
+
return delete_option( self::STORED_VERIFIED_ERRORS_OPTION );
|
526 |
+
}
|
527 |
+
|
528 |
+
/**
|
529 |
+
* Gets an error based on the nonce
|
530 |
+
*
|
531 |
+
* Receives a nonce and finds the related error.
|
532 |
+
*
|
533 |
+
* @since 8.7.0
|
534 |
+
*
|
535 |
+
* @param string $nonce The nonce created for the error we want to get.
|
536 |
+
* @return null|array Returns the error array representation or null if error not found.
|
537 |
+
*/
|
538 |
+
public function get_error_by_nonce( $nonce ) {
|
539 |
+
$errors = $this->get_stored_errors();
|
540 |
+
foreach ( $errors as $user_group ) {
|
541 |
+
foreach ( $user_group as $error ) {
|
542 |
+
if ( $error['nonce'] === $nonce ) {
|
543 |
+
return $error;
|
544 |
+
}
|
545 |
+
}
|
546 |
+
}
|
547 |
+
return null;
|
548 |
+
}
|
549 |
+
|
550 |
+
/**
|
551 |
+
* Adds an error to the verified error list
|
552 |
+
*
|
553 |
+
* @since 8.7.0
|
554 |
+
*
|
555 |
+
* @param array $error The error array, as it was saved in the unverified errors list.
|
556 |
+
* @return void
|
557 |
+
*/
|
558 |
+
public function verify_error( $error ) {
|
559 |
+
|
560 |
+
$verified_errors = $this->get_verified_errors();
|
561 |
+
$error_code = $error['error_code'];
|
562 |
+
$user_id = $error['user_id'];
|
563 |
+
|
564 |
+
if ( ! isset( $verified_errors[ $error_code ] ) ) {
|
565 |
+
$verified_errors[ $error_code ] = array();
|
566 |
+
}
|
567 |
+
|
568 |
+
$verified_errors[ $error_code ][ $user_id ] = $error;
|
569 |
+
|
570 |
+
update_option( self::STORED_VERIFIED_ERRORS_OPTION, $verified_errors );
|
571 |
+
|
572 |
+
}
|
573 |
+
|
574 |
+
/**
|
575 |
+
* Register REST API end point for error hanlding.
|
576 |
+
*
|
577 |
+
* @since 8.7.0
|
578 |
+
*
|
579 |
+
* @return void
|
580 |
+
*/
|
581 |
+
public function register_verify_error_endpoint() {
|
582 |
+
register_rest_route(
|
583 |
+
'jetpack/v4',
|
584 |
+
'/verify_xmlrpc_error',
|
585 |
+
array(
|
586 |
+
'methods' => \WP_REST_Server::CREATABLE,
|
587 |
+
'callback' => array( $this, 'verify_xml_rpc_error' ),
|
588 |
+
'permission_callback' => '__return_true',
|
589 |
+
'args' => array(
|
590 |
+
'nonce' => array(
|
591 |
+
'required' => true,
|
592 |
+
'type' => 'string',
|
593 |
+
),
|
594 |
+
),
|
595 |
+
)
|
596 |
+
);
|
597 |
+
}
|
598 |
+
|
599 |
+
/**
|
600 |
+
* Handles verification that a xml rpc error is legit and came from WordPres.com
|
601 |
+
*
|
602 |
+
* @since 8.7.0
|
603 |
+
*
|
604 |
+
* @param \WP_REST_Request $request The request sent to the WP REST API.
|
605 |
+
*
|
606 |
+
* @return boolean
|
607 |
+
*/
|
608 |
+
public function verify_xml_rpc_error( \WP_REST_Request $request ) {
|
609 |
+
|
610 |
+
$error = $this->get_error_by_nonce( $request['nonce'] );
|
611 |
+
|
612 |
+
if ( $error ) {
|
613 |
+
$this->verify_error( $error );
|
614 |
+
return new \WP_REST_Response( true, 200 );
|
615 |
+
}
|
616 |
+
|
617 |
+
return new \WP_REST_Response( false, 200 );
|
618 |
+
|
619 |
+
}
|
620 |
+
|
621 |
+
}
|
vendor/automattic/jetpack-connection/src/class-manager.php
CHANGED
@@ -9,7 +9,9 @@ namespace Automattic\Jetpack\Connection;
|
|
9 |
|
10 |
use Automattic\Jetpack\Constants;
|
11 |
use Automattic\Jetpack\Roles;
|
|
|
12 |
use Automattic\Jetpack\Tracking;
|
|
|
13 |
|
14 |
/**
|
15 |
* The Jetpack Connection Manager class that is used as a single gateway between WordPress.com
|
@@ -74,32 +76,35 @@ class Manager {
|
|
74 |
public static function configure() {
|
75 |
$manager = new self();
|
76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
$manager->setup_xmlrpc_handlers(
|
78 |
$_GET, // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
79 |
$manager->is_active(),
|
80 |
$manager->verify_xml_rpc_signature()
|
81 |
);
|
82 |
|
|
|
|
|
83 |
if ( $manager->is_active() ) {
|
84 |
add_filter( 'xmlrpc_methods', array( $manager, 'public_xmlrpc_methods' ) );
|
85 |
-
} else {
|
86 |
-
add_action( 'rest_api_init', array( $manager, 'initialize_rest_api_registration_connector' ) );
|
87 |
}
|
88 |
|
|
|
|
|
89 |
add_action( 'jetpack_clean_nonces', array( $manager, 'clean_nonces' ) );
|
90 |
if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
|
91 |
wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
|
92 |
}
|
93 |
|
94 |
-
add_filter(
|
95 |
-
'jetpack_constant_default_value',
|
96 |
-
__NAMESPACE__ . '\Utils::jetpack_api_constant_filter',
|
97 |
-
10,
|
98 |
-
2
|
99 |
-
);
|
100 |
-
|
101 |
add_action( 'plugins_loaded', __NAMESPACE__ . '\Plugin_Storage::configure', 100 );
|
102 |
|
|
|
103 |
}
|
104 |
|
105 |
/**
|
@@ -279,7 +284,7 @@ class Manager {
|
|
279 |
* @param String $password password string.
|
280 |
* @return WP_User|Mixed authenticated user or error.
|
281 |
*/
|
282 |
-
public function authenticate_jetpack( $user, $username, $password ) {
|
283 |
if ( is_a( $user, '\\WP_User' ) ) {
|
284 |
return $user;
|
285 |
}
|
@@ -316,19 +321,14 @@ class Manager {
|
|
316 |
/**
|
317 |
* Action for logging XMLRPC signature verification errors. This data is sensitive.
|
318 |
*
|
319 |
-
* Error codes:
|
320 |
-
* - malformed_token
|
321 |
-
* - malformed_user_id
|
322 |
-
* - unknown_token
|
323 |
-
* - could_not_sign
|
324 |
-
* - invalid_nonce
|
325 |
-
* - signature_mismatch
|
326 |
-
*
|
327 |
* @since 7.5.0
|
328 |
*
|
329 |
* @param WP_Error $signature_verification_error The verification error
|
330 |
*/
|
331 |
do_action( 'jetpack_verify_signature_error', $this->xmlrpc_verification );
|
|
|
|
|
|
|
332 |
}
|
333 |
}
|
334 |
|
@@ -519,9 +519,9 @@ class Manager {
|
|
519 |
* @return bool
|
520 |
*/
|
521 |
public function is_registered() {
|
522 |
-
$
|
523 |
-
$
|
524 |
-
return $
|
525 |
}
|
526 |
|
527 |
/**
|
@@ -728,6 +728,10 @@ class Manager {
|
|
728 |
|
729 |
\Jetpack_Options::update_option( 'user_tokens', $tokens );
|
730 |
|
|
|
|
|
|
|
|
|
731 |
/**
|
732 |
* Fires after the current user has been unlinked from WordPress.com.
|
733 |
*
|
@@ -804,7 +808,7 @@ class Manager {
|
|
804 |
* WordPress.com.
|
805 |
*
|
806 |
* @param String $api_endpoint (optional) an API endpoint to use, defaults to 'register'.
|
807 |
-
* @return
|
808 |
*/
|
809 |
public function register( $api_endpoint = 'register' ) {
|
810 |
add_action( 'pre_update_jetpack_option_register', array( '\\Jetpack_Options', 'delete_option' ) );
|
@@ -843,20 +847,21 @@ class Manager {
|
|
843 |
$body = apply_filters(
|
844 |
'jetpack_register_request_body',
|
845 |
array(
|
846 |
-
'siteurl'
|
847 |
-
'home'
|
848 |
-
'gmt_offset'
|
849 |
-
'timezone_string'
|
850 |
-
'site_name'
|
851 |
-
'secret_1'
|
852 |
-
'secret_2'
|
853 |
-
'site_lang'
|
854 |
-
'timeout'
|
855 |
-
'stats_id'
|
856 |
-
'state'
|
857 |
-
'site_created'
|
858 |
-
'jetpack_version'
|
859 |
-
'ABSPATH'
|
|
|
860 |
)
|
861 |
);
|
862 |
|
@@ -950,7 +955,7 @@ class Manager {
|
|
950 |
* @since 2.6
|
951 |
*
|
952 |
* @param Mixed $response the response object, or the error object.
|
953 |
-
* @return string|WP_Error A JSON object on success or
|
954 |
**/
|
955 |
protected function validate_remote_register_response( $response ) {
|
956 |
if ( is_wp_error( $response ) ) {
|
@@ -1097,6 +1102,46 @@ class Manager {
|
|
1097 |
}
|
1098 |
}
|
1099 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1100 |
/**
|
1101 |
* Builds the timeout limit for queries talking with the wpcom servers.
|
1102 |
*
|
@@ -1319,8 +1364,30 @@ class Manager {
|
|
1319 |
|
1320 |
/**
|
1321 |
* Deletes all connection tokens and transients from the local Jetpack site.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1322 |
*/
|
1323 |
-
public function delete_all_connection_tokens() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1324 |
\Jetpack_Options::delete_option(
|
1325 |
array(
|
1326 |
'blog_token',
|
@@ -1337,14 +1404,74 @@ class Manager {
|
|
1337 |
// Delete cached connected user data.
|
1338 |
$transient_key = 'jetpack_connected_user_data_' . get_current_user_id();
|
1339 |
delete_transient( $transient_key );
|
|
|
|
|
|
|
|
|
|
|
1340 |
}
|
1341 |
|
1342 |
/**
|
1343 |
* Tells WordPress.com to disconnect the site and clear all tokens from cached site.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1344 |
*/
|
1345 |
-
public function disconnect_site_wpcom() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1346 |
$xml = new \Jetpack_IXR_Client();
|
1347 |
$xml->query( 'jetpack.deregister', get_current_user_id() );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1348 |
}
|
1349 |
|
1350 |
/**
|
@@ -2025,23 +2152,26 @@ class Manager {
|
|
2025 |
|
2026 |
if ( $user_id ) {
|
2027 |
if ( ! $user_tokens ) {
|
2028 |
-
return $suppress_errors ? false : new \WP_Error( 'no_user_tokens' );
|
2029 |
}
|
2030 |
if ( self::JETPACK_MASTER_USER === $user_id ) {
|
2031 |
$user_id = \Jetpack_Options::get_option( 'master_user' );
|
2032 |
if ( ! $user_id ) {
|
2033 |
-
return $suppress_errors ? false : new \WP_Error( 'empty_master_user_option' );
|
2034 |
}
|
2035 |
}
|
2036 |
if ( ! isset( $user_tokens[ $user_id ] ) || ! $user_tokens[ $user_id ] ) {
|
2037 |
-
|
|
|
2038 |
}
|
2039 |
$user_token_chunks = explode( '.', $user_tokens[ $user_id ] );
|
2040 |
if ( empty( $user_token_chunks[1] ) || empty( $user_token_chunks[2] ) ) {
|
2041 |
-
|
|
|
2042 |
}
|
2043 |
if ( $user_token_chunks[2] !== (string) $user_id ) {
|
2044 |
-
|
|
|
2045 |
}
|
2046 |
$possible_normal_tokens[] = "{$user_token_chunks[0]}.{$user_token_chunks[1]}";
|
2047 |
} else {
|
@@ -2071,7 +2201,8 @@ class Manager {
|
|
2071 |
}
|
2072 |
|
2073 |
if ( ! $possible_tokens ) {
|
2074 |
-
|
|
|
2075 |
}
|
2076 |
|
2077 |
$valid_token = false;
|
@@ -2096,7 +2227,12 @@ class Manager {
|
|
2096 |
}
|
2097 |
|
2098 |
if ( ! $valid_token ) {
|
2099 |
-
|
|
|
|
|
|
|
|
|
|
|
2100 |
}
|
2101 |
|
2102 |
return (object) array(
|
@@ -2268,12 +2404,64 @@ class Manager {
|
|
2268 |
}
|
2269 |
|
2270 |
/**
|
2271 |
-
* Get all connected plugins information.
|
|
|
|
|
|
|
2272 |
*
|
2273 |
-
* @return array
|
2274 |
*/
|
2275 |
public function get_connected_plugins() {
|
2276 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2277 |
}
|
2278 |
|
2279 |
}
|
9 |
|
10 |
use Automattic\Jetpack\Constants;
|
11 |
use Automattic\Jetpack\Roles;
|
12 |
+
use Automattic\Jetpack\Status;
|
13 |
use Automattic\Jetpack\Tracking;
|
14 |
+
use WP_Error;
|
15 |
|
16 |
/**
|
17 |
* The Jetpack Connection Manager class that is used as a single gateway between WordPress.com
|
76 |
public static function configure() {
|
77 |
$manager = new self();
|
78 |
|
79 |
+
add_filter(
|
80 |
+
'jetpack_constant_default_value',
|
81 |
+
__NAMESPACE__ . '\Utils::jetpack_api_constant_filter',
|
82 |
+
10,
|
83 |
+
2
|
84 |
+
);
|
85 |
+
|
86 |
$manager->setup_xmlrpc_handlers(
|
87 |
$_GET, // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
88 |
$manager->is_active(),
|
89 |
$manager->verify_xml_rpc_signature()
|
90 |
);
|
91 |
|
92 |
+
$manager->error_handler = Error_Handler::get_instance();
|
93 |
+
|
94 |
if ( $manager->is_active() ) {
|
95 |
add_filter( 'xmlrpc_methods', array( $manager, 'public_xmlrpc_methods' ) );
|
|
|
|
|
96 |
}
|
97 |
|
98 |
+
add_action( 'rest_api_init', array( $manager, 'initialize_rest_api_registration_connector' ) );
|
99 |
+
|
100 |
add_action( 'jetpack_clean_nonces', array( $manager, 'clean_nonces' ) );
|
101 |
if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
|
102 |
wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
|
103 |
}
|
104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
add_action( 'plugins_loaded', __NAMESPACE__ . '\Plugin_Storage::configure', 100 );
|
106 |
|
107 |
+
add_filter( 'map_meta_cap', array( $manager, 'jetpack_connection_custom_caps' ), 1, 4 );
|
108 |
}
|
109 |
|
110 |
/**
|
284 |
* @param String $password password string.
|
285 |
* @return WP_User|Mixed authenticated user or error.
|
286 |
*/
|
287 |
+
public function authenticate_jetpack( $user, $username, $password ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
288 |
if ( is_a( $user, '\\WP_User' ) ) {
|
289 |
return $user;
|
290 |
}
|
321 |
/**
|
322 |
* Action for logging XMLRPC signature verification errors. This data is sensitive.
|
323 |
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
324 |
* @since 7.5.0
|
325 |
*
|
326 |
* @param WP_Error $signature_verification_error The verification error
|
327 |
*/
|
328 |
do_action( 'jetpack_verify_signature_error', $this->xmlrpc_verification );
|
329 |
+
|
330 |
+
Error_Handler::get_instance()->report_error( $this->xmlrpc_verification );
|
331 |
+
|
332 |
}
|
333 |
}
|
334 |
|
519 |
* @return bool
|
520 |
*/
|
521 |
public function is_registered() {
|
522 |
+
$has_blog_id = (bool) \Jetpack_Options::get_option( 'id' );
|
523 |
+
$has_blog_token = (bool) $this->get_access_token( false );
|
524 |
+
return $has_blog_id && $has_blog_token;
|
525 |
}
|
526 |
|
527 |
/**
|
728 |
|
729 |
\Jetpack_Options::update_option( 'user_tokens', $tokens );
|
730 |
|
731 |
+
// Delete cached connected user data.
|
732 |
+
$transient_key = "jetpack_connected_user_data_$user_id";
|
733 |
+
delete_transient( $transient_key );
|
734 |
+
|
735 |
/**
|
736 |
* Fires after the current user has been unlinked from WordPress.com.
|
737 |
*
|
808 |
* WordPress.com.
|
809 |
*
|
810 |
* @param String $api_endpoint (optional) an API endpoint to use, defaults to 'register'.
|
811 |
+
* @return true|WP_Error The error object.
|
812 |
*/
|
813 |
public function register( $api_endpoint = 'register' ) {
|
814 |
add_action( 'pre_update_jetpack_option_register', array( '\\Jetpack_Options', 'delete_option' ) );
|
847 |
$body = apply_filters(
|
848 |
'jetpack_register_request_body',
|
849 |
array(
|
850 |
+
'siteurl' => site_url(),
|
851 |
+
'home' => home_url(),
|
852 |
+
'gmt_offset' => $gmt_offset,
|
853 |
+
'timezone_string' => (string) get_option( 'timezone_string' ),
|
854 |
+
'site_name' => (string) get_option( 'blogname' ),
|
855 |
+
'secret_1' => $secrets['secret_1'],
|
856 |
+
'secret_2' => $secrets['secret_2'],
|
857 |
+
'site_lang' => get_locale(),
|
858 |
+
'timeout' => $timeout,
|
859 |
+
'stats_id' => $stats_id,
|
860 |
+
'state' => get_current_user_id(),
|
861 |
+
'site_created' => $this->get_assumed_site_creation_date(),
|
862 |
+
'jetpack_version' => Constants::get_constant( 'JETPACK__VERSION' ),
|
863 |
+
'ABSPATH' => Constants::get_constant( 'ABSPATH' ),
|
864 |
+
'current_user_email' => wp_get_current_user()->user_email,
|
865 |
)
|
866 |
);
|
867 |
|
955 |
* @since 2.6
|
956 |
*
|
957 |
* @param Mixed $response the response object, or the error object.
|
958 |
+
* @return string|WP_Error A JSON object on success or WP_Error on failures
|
959 |
**/
|
960 |
protected function validate_remote_register_response( $response ) {
|
961 |
if ( is_wp_error( $response ) ) {
|
1102 |
}
|
1103 |
}
|
1104 |
|
1105 |
+
/**
|
1106 |
+
* Sets the Connection custom capabilities.
|
1107 |
+
*
|
1108 |
+
* @param string[] $caps Array of the user's capabilities.
|
1109 |
+
* @param string $cap Capability name.
|
1110 |
+
* @param int $user_id The user ID.
|
1111 |
+
* @param array $args Adds the context to the cap. Typically the object ID.
|
1112 |
+
*/
|
1113 |
+
public function jetpack_connection_custom_caps( $caps, $cap, $user_id, $args ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
1114 |
+
$is_offline_mode = ( new Status() )->is_offline_mode();
|
1115 |
+
switch ( $cap ) {
|
1116 |
+
case 'jetpack_connect':
|
1117 |
+
case 'jetpack_reconnect':
|
1118 |
+
if ( $is_offline_mode ) {
|
1119 |
+
$caps = array( 'do_not_allow' );
|
1120 |
+
break;
|
1121 |
+
}
|
1122 |
+
// Pass through. If it's not offline mode, these should match disconnect.
|
1123 |
+
// Let users disconnect if it's offline mode, just in case things glitch.
|
1124 |
+
case 'jetpack_disconnect':
|
1125 |
+
/**
|
1126 |
+
* Filters the jetpack_disconnect capability.
|
1127 |
+
*
|
1128 |
+
* @since 8.7.0
|
1129 |
+
*
|
1130 |
+
* @param array An array containing the capability name.
|
1131 |
+
*/
|
1132 |
+
$caps = apply_filters( 'jetpack_disconnect_cap', array( 'manage_options' ) );
|
1133 |
+
break;
|
1134 |
+
case 'jetpack_connect_user':
|
1135 |
+
if ( $is_offline_mode ) {
|
1136 |
+
$caps = array( 'do_not_allow' );
|
1137 |
+
break;
|
1138 |
+
}
|
1139 |
+
$caps = array( 'read' );
|
1140 |
+
break;
|
1141 |
+
}
|
1142 |
+
return $caps;
|
1143 |
+
}
|
1144 |
+
|
1145 |
/**
|
1146 |
* Builds the timeout limit for queries talking with the wpcom servers.
|
1147 |
*
|
1364 |
|
1365 |
/**
|
1366 |
* Deletes all connection tokens and transients from the local Jetpack site.
|
1367 |
+
* If the plugin object has been provided in the constructor, the function first checks
|
1368 |
+
* whether it's the only active connection.
|
1369 |
+
* If there are any other connections, the function will do nothing and return `false`
|
1370 |
+
* (unless `$ignore_connected_plugins` is set to `true`).
|
1371 |
+
*
|
1372 |
+
* @param bool $ignore_connected_plugins Delete the tokens even if there are other connected plugins.
|
1373 |
+
*
|
1374 |
+
* @return bool True if disconnected successfully, false otherwise.
|
1375 |
*/
|
1376 |
+
public function delete_all_connection_tokens( $ignore_connected_plugins = false ) {
|
1377 |
+
if ( ! $ignore_connected_plugins && null !== $this->plugin && ! $this->plugin->is_only() ) {
|
1378 |
+
return false;
|
1379 |
+
}
|
1380 |
+
|
1381 |
+
/**
|
1382 |
+
* Fires upon the disconnect attempt.
|
1383 |
+
* Return `false` to prevent the disconnect.
|
1384 |
+
*
|
1385 |
+
* @since 8.7.0
|
1386 |
+
*/
|
1387 |
+
if ( ! apply_filters( 'jetpack_connection_delete_all_tokens', true, $this ) ) {
|
1388 |
+
return false;
|
1389 |
+
}
|
1390 |
+
|
1391 |
\Jetpack_Options::delete_option(
|
1392 |
array(
|
1393 |
'blog_token',
|
1404 |
// Delete cached connected user data.
|
1405 |
$transient_key = 'jetpack_connected_user_data_' . get_current_user_id();
|
1406 |
delete_transient( $transient_key );
|
1407 |
+
|
1408 |
+
// Delete all XML-RPC errors.
|
1409 |
+
Error_Handler::get_instance()->delete_all_errors();
|
1410 |
+
|
1411 |
+
return true;
|
1412 |
}
|
1413 |
|
1414 |
/**
|
1415 |
* Tells WordPress.com to disconnect the site and clear all tokens from cached site.
|
1416 |
+
* If the plugin object has been provided in the constructor, the function first check
|
1417 |
+
* whether it's the only active connection.
|
1418 |
+
* If there are any other connections, the function will do nothing and return `false`
|
1419 |
+
* (unless `$ignore_connected_plugins` is set to `true`).
|
1420 |
+
*
|
1421 |
+
* @param bool $ignore_connected_plugins Delete the tokens even if there are other connected plugins.
|
1422 |
+
*
|
1423 |
+
* @return bool True if disconnected successfully, false otherwise.
|
1424 |
*/
|
1425 |
+
public function disconnect_site_wpcom( $ignore_connected_plugins = false ) {
|
1426 |
+
if ( ! $ignore_connected_plugins && null !== $this->plugin && ! $this->plugin->is_only() ) {
|
1427 |
+
return false;
|
1428 |
+
}
|
1429 |
+
|
1430 |
+
/**
|
1431 |
+
* Fires upon the disconnect attempt.
|
1432 |
+
* Return `false` to prevent the disconnect.
|
1433 |
+
*
|
1434 |
+
* @since 8.7.0
|
1435 |
+
*/
|
1436 |
+
if ( ! apply_filters( 'jetpack_connection_disconnect_site_wpcom', true, $this ) ) {
|
1437 |
+
return false;
|
1438 |
+
}
|
1439 |
+
|
1440 |
$xml = new \Jetpack_IXR_Client();
|
1441 |
$xml->query( 'jetpack.deregister', get_current_user_id() );
|
1442 |
+
|
1443 |
+
return true;
|
1444 |
+
}
|
1445 |
+
|
1446 |
+
/**
|
1447 |
+
* Disconnect the plugin and remove the tokens.
|
1448 |
+
* This function will automatically perform "soft" or "hard" disconnect depending on whether other plugins are using the connection.
|
1449 |
+
* This is a proxy method to simplify the Connection package API.
|
1450 |
+
*
|
1451 |
+
* @see Manager::disable_plugin()
|
1452 |
+
* @see Manager::disconnect_site_wpcom()
|
1453 |
+
* @see Manager::delete_all_connection_tokens()
|
1454 |
+
*
|
1455 |
+
* @return bool
|
1456 |
+
*/
|
1457 |
+
public function remove_connection() {
|
1458 |
+
$this->disable_plugin();
|
1459 |
+
$this->disconnect_site_wpcom();
|
1460 |
+
$this->delete_all_connection_tokens();
|
1461 |
+
|
1462 |
+
return true;
|
1463 |
+
}
|
1464 |
+
|
1465 |
+
/**
|
1466 |
+
* Completely clearing up the connection, and initiating reconnect.
|
1467 |
+
*
|
1468 |
+
* @return true|WP_Error True if reconnected successfully, a `WP_Error` object otherwise.
|
1469 |
+
*/
|
1470 |
+
public function reconnect() {
|
1471 |
+
$this->disconnect_site_wpcom( true );
|
1472 |
+
$this->delete_all_connection_tokens( true );
|
1473 |
+
|
1474 |
+
return $this->register();
|
1475 |
}
|
1476 |
|
1477 |
/**
|
2152 |
|
2153 |
if ( $user_id ) {
|
2154 |
if ( ! $user_tokens ) {
|
2155 |
+
return $suppress_errors ? false : new \WP_Error( 'no_user_tokens', __( 'No user tokens found', 'jetpack' ) );
|
2156 |
}
|
2157 |
if ( self::JETPACK_MASTER_USER === $user_id ) {
|
2158 |
$user_id = \Jetpack_Options::get_option( 'master_user' );
|
2159 |
if ( ! $user_id ) {
|
2160 |
+
return $suppress_errors ? false : new \WP_Error( 'empty_master_user_option', __( 'No primary user defined', 'jetpack' ) );
|
2161 |
}
|
2162 |
}
|
2163 |
if ( ! isset( $user_tokens[ $user_id ] ) || ! $user_tokens[ $user_id ] ) {
|
2164 |
+
// translators: %s is the user ID.
|
2165 |
+
return $suppress_errors ? false : new \WP_Error( 'no_token_for_user', sprintf( __( 'No token for user %d', 'jetpack' ), $user_id ) );
|
2166 |
}
|
2167 |
$user_token_chunks = explode( '.', $user_tokens[ $user_id ] );
|
2168 |
if ( empty( $user_token_chunks[1] ) || empty( $user_token_chunks[2] ) ) {
|
2169 |
+
// translators: %s is the user ID.
|
2170 |
+
return $suppress_errors ? false : new \WP_Error( 'token_malformed', sprintf( __( 'Token for user %d is malformed', 'jetpack' ), $user_id ) );
|
2171 |
}
|
2172 |
if ( $user_token_chunks[2] !== (string) $user_id ) {
|
2173 |
+
// translators: %1$d is the ID of the requested user. %2$d is the user ID found in the token.
|
2174 |
+
return $suppress_errors ? false : new \WP_Error( 'user_id_mismatch', sprintf( __( 'Requesting user_id %1$d does not match token user_id %2$d', 'jetpack' ), $user_id, $user_token_chunks[2] ) );
|
2175 |
}
|
2176 |
$possible_normal_tokens[] = "{$user_token_chunks[0]}.{$user_token_chunks[1]}";
|
2177 |
} else {
|
2201 |
}
|
2202 |
|
2203 |
if ( ! $possible_tokens ) {
|
2204 |
+
// If no user tokens were found, it would have failed earlier, so this is about blog token.
|
2205 |
+
return $suppress_errors ? false : new \WP_Error( 'no_possible_tokens', __( 'No blog token found', 'jetpack' ) );
|
2206 |
}
|
2207 |
|
2208 |
$valid_token = false;
|
2227 |
}
|
2228 |
|
2229 |
if ( ! $valid_token ) {
|
2230 |
+
if ( $user_id ) {
|
2231 |
+
// translators: %d is the user ID.
|
2232 |
+
return $suppress_errors ? false : new \WP_Error( 'no_valid_token', sprintf( __( 'Invalid token for user %d', 'jetpack' ), $user_id ) );
|
2233 |
+
} else {
|
2234 |
+
return $suppress_errors ? false : new \WP_Error( 'no_valid_token', __( 'Invalid blog token', 'jetpack' ) );
|
2235 |
+
}
|
2236 |
}
|
2237 |
|
2238 |
return (object) array(
|
2404 |
}
|
2405 |
|
2406 |
/**
|
2407 |
+
* Get all connected plugins information, excluding those disconnected by user.
|
2408 |
+
* WARNING: the method cannot be called until Plugin_Storage::configure is called, which happens on plugins_loaded
|
2409 |
+
* Even if you don't use Jetpack Config, it may be introduced later by other plugins,
|
2410 |
+
* so please make sure not to run the method too early in the code.
|
2411 |
*
|
2412 |
+
* @return array|WP_Error
|
2413 |
*/
|
2414 |
public function get_connected_plugins() {
|
2415 |
+
$maybe_plugins = Plugin_Storage::get_all( true );
|
2416 |
+
|
2417 |
+
if ( $maybe_plugins instanceof WP_Error ) {
|
2418 |
+
return $maybe_plugins;
|
2419 |
+
}
|
2420 |
+
|
2421 |
+
return $maybe_plugins;
|
2422 |
+
}
|
2423 |
+
|
2424 |
+
/**
|
2425 |
+
* Force plugin disconnect. After its called, the plugin will not be allowed to use the connection.
|
2426 |
+
* Note: this method does not remove any access tokens.
|
2427 |
+
*
|
2428 |
+
* @return bool
|
2429 |
+
*/
|
2430 |
+
public function disable_plugin() {
|
2431 |
+
if ( ! $this->plugin ) {
|
2432 |
+
return false;
|
2433 |
+
}
|
2434 |
+
|
2435 |
+
return $this->plugin->disable();
|
2436 |
+
}
|
2437 |
+
|
2438 |
+
/**
|
2439 |
+
* Force plugin reconnect after user-initiated disconnect.
|
2440 |
+
* After its called, the plugin will be allowed to use the connection again.
|
2441 |
+
* Note: this method does not initialize access tokens.
|
2442 |
+
*
|
2443 |
+
* @return bool
|
2444 |
+
*/
|
2445 |
+
public function enable_plugin() {
|
2446 |
+
if ( ! $this->plugin ) {
|
2447 |
+
return false;
|
2448 |
+
}
|
2449 |
+
|
2450 |
+
return $this->plugin->enable();
|
2451 |
+
}
|
2452 |
+
|
2453 |
+
/**
|
2454 |
+
* Whether the plugin is allowed to use the connection, or it's been disconnected by user.
|
2455 |
+
* If no plugin slug was passed into the constructor, always returns true.
|
2456 |
+
*
|
2457 |
+
* @return bool
|
2458 |
+
*/
|
2459 |
+
public function is_plugin_enabled() {
|
2460 |
+
if ( ! $this->plugin ) {
|
2461 |
+
return true;
|
2462 |
+
}
|
2463 |
+
|
2464 |
+
return $this->plugin->is_enabled();
|
2465 |
}
|
2466 |
|
2467 |
}
|
vendor/automattic/jetpack-connection/src/class-plugin-storage.php
CHANGED
@@ -7,20 +7,17 @@
|
|
7 |
|
8 |
namespace Automattic\Jetpack\Connection;
|
9 |
|
10 |
-
use Automattic\Jetpack\Config;
|
11 |
use WP_Error;
|
12 |
|
13 |
/**
|
14 |
-
* The class serves a single purpose - to store the data
|
15 |
-
* Well, we don't really store all that. The information is provided on runtime,
|
16 |
-
* so all we need to do is to save the data into the class property and retrieve it from there on demand.
|
17 |
-
*
|
18 |
-
* @todo Adapt for multisite installations.
|
19 |
*/
|
20 |
class Plugin_Storage {
|
21 |
|
22 |
const ACTIVE_PLUGINS_OPTION_NAME = 'jetpack_connection_active_plugins';
|
23 |
|
|
|
|
|
24 |
/**
|
25 |
* Whether this class was configured for the first time or not.
|
26 |
*
|
@@ -42,14 +39,6 @@ class Plugin_Storage {
|
|
42 |
*/
|
43 |
private static $plugins = array();
|
44 |
|
45 |
-
/**
|
46 |
-
* Whether the plugins were configured.
|
47 |
-
* To make sure we don't call the configuration process again and again.
|
48 |
-
*
|
49 |
-
* @var bool
|
50 |
-
*/
|
51 |
-
private static $plugins_configuration_finished = false;
|
52 |
-
|
53 |
/**
|
54 |
* Add or update the plugin information in the storage.
|
55 |
*
|
@@ -62,7 +51,7 @@ class Plugin_Storage {
|
|
62 |
self::$plugins[ $slug ] = $args;
|
63 |
|
64 |
// if plugin is not in the list of active plugins, refresh the list.
|
65 |
-
if ( ! array_key_exists( $slug, get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() ) ) ) {
|
66 |
self::$refresh_connected_plugins = true;
|
67 |
}
|
68 |
|
@@ -95,16 +84,18 @@ class Plugin_Storage {
|
|
95 |
* Even if you don't use Jetpack Config, it may be introduced later by other plugins,
|
96 |
* so please make sure not to run the method too early in the code.
|
97 |
*
|
|
|
|
|
98 |
* @return array|WP_Error
|
99 |
*/
|
100 |
-
public static function get_all() {
|
101 |
$maybe_error = self::ensure_configured();
|
102 |
|
103 |
if ( $maybe_error instanceof WP_Error ) {
|
104 |
return $maybe_error;
|
105 |
}
|
106 |
|
107 |
-
return self::$plugins;
|
108 |
}
|
109 |
|
110 |
/**
|
@@ -156,7 +147,7 @@ class Plugin_Storage {
|
|
156 |
}
|
157 |
|
158 |
// If a plugin was activated or deactivated.
|
159 |
-
$number_of_plugins_differ = count( self::$plugins ) !== count( get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() ) );
|
160 |
|
161 |
if ( $number_of_plugins_differ || true === self::$refresh_connected_plugins ) {
|
162 |
self::update_active_plugins_option();
|
@@ -176,4 +167,50 @@ class Plugin_Storage {
|
|
176 |
update_option( self::ACTIVE_PLUGINS_OPTION_NAME, self::$plugins );
|
177 |
}
|
178 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
}
|
7 |
|
8 |
namespace Automattic\Jetpack\Connection;
|
9 |
|
|
|
10 |
use WP_Error;
|
11 |
|
12 |
/**
|
13 |
+
* The class serves a single purpose - to store the data which plugins use the connection, along with some auxiliary information.
|
|
|
|
|
|
|
|
|
14 |
*/
|
15 |
class Plugin_Storage {
|
16 |
|
17 |
const ACTIVE_PLUGINS_OPTION_NAME = 'jetpack_connection_active_plugins';
|
18 |
|
19 |
+
const PLUGINS_DISABLED_OPTION_NAME = 'jetpack_connection_disabled_plugins';
|
20 |
+
|
21 |
/**
|
22 |
* Whether this class was configured for the first time or not.
|
23 |
*
|
39 |
*/
|
40 |
private static $plugins = array();
|
41 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
/**
|
43 |
* Add or update the plugin information in the storage.
|
44 |
*
|
51 |
self::$plugins[ $slug ] = $args;
|
52 |
|
53 |
// if plugin is not in the list of active plugins, refresh the list.
|
54 |
+
if ( ! array_key_exists( $slug, (array) get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() ) ) ) {
|
55 |
self::$refresh_connected_plugins = true;
|
56 |
}
|
57 |
|
84 |
* Even if you don't use Jetpack Config, it may be introduced later by other plugins,
|
85 |
* so please make sure not to run the method too early in the code.
|
86 |
*
|
87 |
+
* @param bool $connected_only Exclude plugins that were explicitly disconnected.
|
88 |
+
*
|
89 |
* @return array|WP_Error
|
90 |
*/
|
91 |
+
public static function get_all( $connected_only = false ) {
|
92 |
$maybe_error = self::ensure_configured();
|
93 |
|
94 |
if ( $maybe_error instanceof WP_Error ) {
|
95 |
return $maybe_error;
|
96 |
}
|
97 |
|
98 |
+
return $connected_only ? array_diff_key( self::$plugins, array_flip( self::get_all_disabled_plugins() ) ) : self::$plugins;
|
99 |
}
|
100 |
|
101 |
/**
|
147 |
}
|
148 |
|
149 |
// If a plugin was activated or deactivated.
|
150 |
+
$number_of_plugins_differ = count( self::$plugins ) !== count( (array) get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() ) );
|
151 |
|
152 |
if ( $number_of_plugins_differ || true === self::$refresh_connected_plugins ) {
|
153 |
self::update_active_plugins_option();
|
167 |
update_option( self::ACTIVE_PLUGINS_OPTION_NAME, self::$plugins );
|
168 |
}
|
169 |
|
170 |
+
/**
|
171 |
+
* Add the plugin to the set of disconnected ones.
|
172 |
+
*
|
173 |
+
* @param string $slug Plugin slug.
|
174 |
+
*
|
175 |
+
* @return bool
|
176 |
+
*/
|
177 |
+
public static function disable_plugin( $slug ) {
|
178 |
+
$disconnects = self::get_all_disabled_plugins();
|
179 |
+
|
180 |
+
if ( ! in_array( $slug, $disconnects, true ) ) {
|
181 |
+
$disconnects[] = $slug;
|
182 |
+
update_option( self::PLUGINS_DISABLED_OPTION_NAME, $disconnects );
|
183 |
+
}
|
184 |
+
|
185 |
+
return true;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Remove the plugin from the set of disconnected ones.
|
190 |
+
*
|
191 |
+
* @param string $slug Plugin slug.
|
192 |
+
*
|
193 |
+
* @return bool
|
194 |
+
*/
|
195 |
+
public static function enable_plugin( $slug ) {
|
196 |
+
$disconnects = self::get_all_disabled_plugins();
|
197 |
+
|
198 |
+
$slug_index = array_search( $slug, $disconnects, true );
|
199 |
+
if ( false !== $slug_index ) {
|
200 |
+
unset( $disconnects[ $slug_index ] );
|
201 |
+
update_option( self::PLUGINS_DISABLED_OPTION_NAME, $disconnects );
|
202 |
+
}
|
203 |
+
|
204 |
+
return true;
|
205 |
+
}
|
206 |
+
|
207 |
+
/**
|
208 |
+
* Get all plugins that were disconnected by user.
|
209 |
+
*
|
210 |
+
* @return array
|
211 |
+
*/
|
212 |
+
public static function get_all_disabled_plugins() {
|
213 |
+
return (array) get_option( self::PLUGINS_DISABLED_OPTION_NAME, array() );
|
214 |
+
}
|
215 |
+
|
216 |
}
|
vendor/automattic/jetpack-connection/src/class-plugin.php
CHANGED
@@ -74,9 +74,36 @@ class Plugin {
|
|
74 |
* @return bool
|
75 |
*/
|
76 |
public function is_only() {
|
77 |
-
$plugins = Plugin_Storage::get_all();
|
78 |
|
79 |
return ! $plugins || ( array_key_exists( $this->slug, $plugins ) && 1 === count( $plugins ) );
|
80 |
}
|
81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
}
|
74 |
* @return bool
|
75 |
*/
|
76 |
public function is_only() {
|
77 |
+
$plugins = Plugin_Storage::get_all( true );
|
78 |
|
79 |
return ! $plugins || ( array_key_exists( $this->slug, $plugins ) && 1 === count( $plugins ) );
|
80 |
}
|
81 |
|
82 |
+
/**
|
83 |
+
* Add the plugin to the set of disconnected ones.
|
84 |
+
*
|
85 |
+
* @return bool
|
86 |
+
*/
|
87 |
+
public function disable() {
|
88 |
+
return Plugin_Storage::disable_plugin( $this->slug );
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Remove the plugin from the set of disconnected ones.
|
93 |
+
*
|
94 |
+
* @return bool
|
95 |
+
*/
|
96 |
+
public function enable() {
|
97 |
+
return Plugin_Storage::enable_plugin( $this->slug );
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Whether this plugin is allowed to use the connection.
|
102 |
+
*
|
103 |
+
* @return bool
|
104 |
+
*/
|
105 |
+
public function is_enabled() {
|
106 |
+
return ! in_array( $this->slug, Plugin_Storage::get_all_disabled_plugins(), true );
|
107 |
+
}
|
108 |
+
|
109 |
}
|
vendor/automattic/jetpack-connection/src/class-rest-connector.php
CHANGED
@@ -7,6 +7,13 @@
|
|
7 |
|
8 |
namespace Automattic\Jetpack\Connection;
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
/**
|
11 |
* Registers the REST routes for Connections.
|
12 |
*/
|
@@ -18,6 +25,13 @@ class REST_Connector {
|
|
18 |
*/
|
19 |
private $connection;
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
/**
|
22 |
* Constructor.
|
23 |
*
|
@@ -26,13 +40,72 @@ class REST_Connector {
|
|
26 |
public function __construct( Manager $connection ) {
|
27 |
$this->connection = $connection;
|
28 |
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
register_rest_route(
|
31 |
'jetpack/v4',
|
32 |
-
'/
|
33 |
array(
|
34 |
-
'methods'
|
35 |
-
'callback'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
)
|
37 |
);
|
38 |
}
|
@@ -42,13 +115,168 @@ class REST_Connector {
|
|
42 |
*
|
43 |
* @since 5.4.0
|
44 |
*
|
45 |
-
* @param
|
46 |
*
|
47 |
* @return string|WP_Error
|
48 |
*/
|
49 |
-
public function verify_registration(
|
50 |
$registration_data = array( $request['secret_1'], $request['state'] );
|
51 |
|
52 |
return $this->connection->handle_registration( $registration_data );
|
53 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
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 |
*/
|
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 |
*
|
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 |
+
'args' => array(
|
103 |
+
'action' => array(
|
104 |
+
'type' => 'string',
|
105 |
+
'required' => true,
|
106 |
+
),
|
107 |
+
),
|
108 |
+
'permission_callback' => __CLASS__ . '::jetpack_disconnect_permission_check',
|
109 |
)
|
110 |
);
|
111 |
}
|
115 |
*
|
116 |
* @since 5.4.0
|
117 |
*
|
118 |
+
* @param WP_REST_Request $request The request sent to the WP REST API.
|
119 |
*
|
120 |
* @return string|WP_Error
|
121 |
*/
|
122 |
+
public function verify_registration( WP_REST_Request $request ) {
|
123 |
$registration_data = array( $request['secret_1'], $request['state'] );
|
124 |
|
125 |
return $this->connection->handle_registration( $registration_data );
|
126 |
}
|
127 |
+
|
128 |
+
/**
|
129 |
+
* Handles verification that a site is registered
|
130 |
+
*
|
131 |
+
* @since 5.4.0
|
132 |
+
*
|
133 |
+
* @param WP_REST_Request $request The request sent to the WP REST API.
|
134 |
+
*
|
135 |
+
* @return array|wp-error
|
136 |
+
*/
|
137 |
+
public static function remote_authorize( $request ) {
|
138 |
+
$xmlrpc_server = new Jetpack_XMLRPC_Server();
|
139 |
+
$result = $xmlrpc_server->remote_authorize( $request );
|
140 |
+
|
141 |
+
if ( is_a( $result, 'IXR_Error' ) ) {
|
142 |
+
$result = new WP_Error( $result->code, $result->message );
|
143 |
+
}
|
144 |
+
|
145 |
+
return $result;
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Get connection status for this Jetpack site.
|
150 |
+
*
|
151 |
+
* @since 4.3.0
|
152 |
+
*
|
153 |
+
* @param bool $rest_response Should we return a rest response or a simple array. Default to rest response.
|
154 |
+
*
|
155 |
+
* @return WP_REST_Response|array Connection information.
|
156 |
+
*/
|
157 |
+
public static function connection_status( $rest_response = true ) {
|
158 |
+
$status = new Status();
|
159 |
+
$connection = new Manager();
|
160 |
+
|
161 |
+
$connection_status = array(
|
162 |
+
'isActive' => $connection->is_active(),
|
163 |
+
'isStaging' => $status->is_staging_site(),
|
164 |
+
'isRegistered' => $connection->is_registered(),
|
165 |
+
'offlineMode' => array(
|
166 |
+
'isActive' => $status->is_offline_mode(),
|
167 |
+
'constant' => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,
|
168 |
+
'url' => $status->is_local_site(),
|
169 |
+
/** This filter is documented in packages/status/src/class-status.php */
|
170 |
+
'filter' => ( apply_filters( 'jetpack_development_mode', false ) || apply_filters( 'jetpack_offline_mode', false ) ), // jetpack_development_mode is deprecated.
|
171 |
+
'wpLocalConstant' => defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV,
|
172 |
+
),
|
173 |
+
'isPublic' => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
|
174 |
+
);
|
175 |
+
|
176 |
+
if ( $rest_response ) {
|
177 |
+
return rest_ensure_response(
|
178 |
+
$connection_status
|
179 |
+
);
|
180 |
+
} else {
|
181 |
+
return $connection_status;
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Get plugins connected to the Jetpack.
|
188 |
+
*
|
189 |
+
* @since 8.6.0
|
190 |
+
*
|
191 |
+
* @return WP_REST_Response|WP_Error Response or error object, depending on the request result.
|
192 |
+
*/
|
193 |
+
public function get_connection_plugins() {
|
194 |
+
$plugins = $this->connection->get_connected_plugins();
|
195 |
+
|
196 |
+
if ( is_wp_error( $plugins ) ) {
|
197 |
+
return $plugins;
|
198 |
+
}
|
199 |
+
|
200 |
+
array_walk(
|
201 |
+
$plugins,
|
202 |
+
function( &$data, $slug ) {
|
203 |
+
$data['slug'] = $slug;
|
204 |
+
}
|
205 |
+
);
|
206 |
+
|
207 |
+
return rest_ensure_response( array_values( $plugins ) );
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Verify that user can view Jetpack admin page and can activate plugins.
|
212 |
+
*
|
213 |
+
* @since 8.8.0
|
214 |
+
*
|
215 |
+
* @return bool|WP_Error Whether user has the capability 'activate_plugins'.
|
216 |
+
*/
|
217 |
+
public static function activate_plugins_permission_check() {
|
218 |
+
if ( current_user_can( 'activate_plugins' ) ) {
|
219 |
+
return true;
|
220 |
+
}
|
221 |
+
|
222 |
+
return new WP_Error( 'invalid_user_permission_activate_plugins', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Verify that user is allowed to disconnect Jetpack.
|
227 |
+
*
|
228 |
+
* @since 8.8.0
|
229 |
+
*
|
230 |
+
* @return bool|WP_Error Whether user has the capability 'jetpack_disconnect'.
|
231 |
+
*/
|
232 |
+
public static function jetpack_disconnect_permission_check() {
|
233 |
+
if ( current_user_can( 'jetpack_disconnect' ) ) {
|
234 |
+
return true;
|
235 |
+
}
|
236 |
+
|
237 |
+
return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
|
238 |
+
}
|
239 |
+
|
240 |
+
/**
|
241 |
+
* Returns generic error message when user is not allowed to perform an action.
|
242 |
+
*
|
243 |
+
* @return string The error message.
|
244 |
+
*/
|
245 |
+
public static function get_user_permissions_error_msg() {
|
246 |
+
return self::$user_permissions_error_msg;
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* The endpoint tried to partially or fully reconnect the website to WP.com.
|
251 |
+
*
|
252 |
+
* @since 8.8.0
|
253 |
+
*
|
254 |
+
* @param WP_REST_Request $request The request sent to the WP REST API.
|
255 |
+
*
|
256 |
+
* @return \WP_REST_Response|WP_Error
|
257 |
+
*/
|
258 |
+
public function connection_reconnect( WP_REST_Request $request ) {
|
259 |
+
$params = $request->get_json_params();
|
260 |
+
|
261 |
+
$response = array();
|
262 |
+
|
263 |
+
switch ( $params['action'] ) {
|
264 |
+
case 'reconnect':
|
265 |
+
$result = $this->connection->reconnect();
|
266 |
+
|
267 |
+
if ( true === $result ) {
|
268 |
+
$response['status'] = 'in_progress';
|
269 |
+
$response['authorizeUrl'] = $this->connection->get_authorization_url();
|
270 |
+
} elseif ( is_wp_error( $result ) ) {
|
271 |
+
$response = $result;
|
272 |
+
}
|
273 |
+
break;
|
274 |
+
default:
|
275 |
+
$response = new WP_Error( 'Unknown action' );
|
276 |
+
break;
|
277 |
+
}
|
278 |
+
|
279 |
+
return rest_ensure_response( $response );
|
280 |
+
}
|
281 |
+
|
282 |
}
|
vendor/automattic/jetpack-connection/src/error-handlers/class-invalid-blog-token.php
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* The Jetpack Connection error handler class for invalid blog tokens
|
4 |
+
*
|
5 |
+
* @package automattic/jetpack-connection
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Automattic\Jetpack\Connection\Error_Handlers;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* This class handles all the error codes that indicates a broken blog token and
|
12 |
+
* suggests the user to reconnect.
|
13 |
+
*
|
14 |
+
* @since 8.7.0
|
15 |
+
*/
|
16 |
+
class Invalid_Blog_Token {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Set up hooks
|
20 |
+
*
|
21 |
+
* @since 8.7.0
|
22 |
+
*
|
23 |
+
* @param array $errors The array containing verified errors stored in the database.
|
24 |
+
*/
|
25 |
+
public function __construct( $errors ) {
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Filters the message to be displayed in the admin notices area when there's a invalid blog token xmlrpc error
|
29 |
+
*
|
30 |
+
* Return an empty value to disable the message.
|
31 |
+
*
|
32 |
+
* @since 8.7.0
|
33 |
+
*
|
34 |
+
* @param string $message The error message.
|
35 |
+
*/
|
36 |
+
$this->message = apply_filters( 'jetpack_connection_invalid_blog_token_admin_notice', __( 'Your connection with WordPress.com seems to be broken. If you\'re experiencing issues, please try reconnecting.', 'jetpack' ) );
|
37 |
+
|
38 |
+
if ( empty( $this->message ) ) {
|
39 |
+
return;
|
40 |
+
}
|
41 |
+
|
42 |
+
// In this class, we will only handle errors with the blog token, so ignoring if there are only errors with user tokens.
|
43 |
+
if ( ! isset( $errors[0] ) && ! isset( $errors['invalid'] ) ) {
|
44 |
+
return;
|
45 |
+
}
|
46 |
+
|
47 |
+
add_action( 'react_connection_errors_initial_state', array( $this, 'jetpack_react_dashboard_error' ) );
|
48 |
+
// do not add admin notice to the jetpack dashboard.
|
49 |
+
global $pagenow;
|
50 |
+
if ( 'admin.php' === $pagenow || isset( $_GET['page'] ) && 'jetpack' === $_GET['page'] ) { // phpcs:ignore
|
51 |
+
return;
|
52 |
+
}
|
53 |
+
add_action( 'admin_notices', array( $this, 'admin_notice' ) );
|
54 |
+
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Prints an admin notice for the blog token error
|
59 |
+
*
|
60 |
+
* @since 8.7.0
|
61 |
+
*
|
62 |
+
* @return void
|
63 |
+
*/
|
64 |
+
public function admin_notice() {
|
65 |
+
|
66 |
+
if ( ! current_user_can( 'jetpack_connect' ) ) {
|
67 |
+
return;
|
68 |
+
}
|
69 |
+
|
70 |
+
?>
|
71 |
+
<div class="notice notice-error is-dismissible jetpack-message jp-connect" style="display:block !important;">
|
72 |
+
<p><?php echo esc_html( $this->message ); ?></p>
|
73 |
+
</div>
|
74 |
+
<?php
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Adds the error message to the Jetpack React Dashboard
|
79 |
+
*
|
80 |
+
* @param array $errors The array of errors.
|
81 |
+
* @return array
|
82 |
+
*/
|
83 |
+
public function jetpack_react_dashboard_error( $errors ) {
|
84 |
+
|
85 |
+
$errors[] = array(
|
86 |
+
'code' => 'invalid_blog_token',
|
87 |
+
'message' => $this->message,
|
88 |
+
'action' => 'reconnect',
|
89 |
+
);
|
90 |
+
return $errors;
|
91 |
+
}
|
92 |
+
|
93 |
+
|
94 |
+
}
|
vendor/automattic/jetpack-constants/phpunit.xml
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<phpunit bootstrap="tests/php/bootstrap.php" backupGlobals="false" colors="true">
|
2 |
+
<testsuites>
|
3 |
+
<testsuite name="main">
|
4 |
+
<directory prefix="test" suffix=".php">tests/php</directory>
|
5 |
+
</testsuite>
|
6 |
+
</testsuites>
|
7 |
+
</phpunit>
|
vendor/automattic/jetpack-options/composer.json
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
"type": "library",
|
5 |
"license": "GPL-2.0-or-later",
|
6 |
"require": {
|
7 |
-
"automattic/jetpack-constants": "1.
|
8 |
},
|
9 |
"require-dev": {
|
10 |
"phpunit/phpunit": "7.*.*",
|
4 |
"type": "library",
|
5 |
"license": "GPL-2.0-or-later",
|
6 |
"require": {
|
7 |
+
"automattic/jetpack-constants": "1.4.0"
|
8 |
},
|
9 |
"require-dev": {
|
10 |
"phpunit/phpunit": "7.*.*",
|
vendor/automattic/jetpack-options/legacy/class-jetpack-options.php
CHANGED
@@ -167,7 +167,8 @@ class Jetpack_Options {
|
|
167 |
}
|
168 |
|
169 |
/**
|
170 |
-
*
|
|
|
171 |
*
|
172 |
* @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
|
173 |
* @param mixed $default (optional).
|
@@ -175,6 +176,28 @@ class Jetpack_Options {
|
|
175 |
* @return mixed
|
176 |
*/
|
177 |
public static function get_option( $name, $default = false ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
178 |
if ( self::is_valid( $name, 'non_compact' ) ) {
|
179 |
if ( self::is_network_option( $name ) ) {
|
180 |
return get_site_option( "jetpack_$name", $default );
|
@@ -481,7 +504,7 @@ class Jetpack_Options {
|
|
481 |
|
482 |
/**
|
483 |
* This function checks for a constant that, if present, will disable direct DB queries Jetpack uses to manage certain options and force Jetpack to always use Options API instead.
|
484 |
-
* Options can be selectively managed via a
|
485 |
*
|
486 |
* @param string $name Option name.
|
487 |
*
|
@@ -497,7 +520,7 @@ class Jetpack_Options {
|
|
497 |
*
|
498 |
* @since 5.5.0
|
499 |
*
|
500 |
-
* @param array $disabled_raw_options An array of option names that you can selectively
|
501 |
*/
|
502 |
$disabled_raw_options = apply_filters( 'jetpack_disabled_raw_options', array() );
|
503 |
return isset( $disabled_raw_options[ $name ] );
|
167 |
}
|
168 |
|
169 |
/**
|
170 |
+
* Filters the requested option.
|
171 |
+
* This is a wrapper around `get_option_from_database` so that we can filter the option.
|
172 |
*
|
173 |
* @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
|
174 |
* @param mixed $default (optional).
|
176 |
* @return mixed
|
177 |
*/
|
178 |
public static function get_option( $name, $default = false ) {
|
179 |
+
/**
|
180 |
+
* Filter Jetpack Options.
|
181 |
+
* Can be useful in environments when Jetpack is running with a different setup
|
182 |
+
*
|
183 |
+
* @since 8.8.0
|
184 |
+
*
|
185 |
+
* @param string $value The value from the database.
|
186 |
+
* @param string $name Option name, _without_ `jetpack_%` prefix.
|
187 |
+
* @return string $value, unless the filters modify it.
|
188 |
+
*/
|
189 |
+
return apply_filters( 'jetpack_options', self::get_option_from_database( $name, $default ), $name );
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* Returns the requested option. Looks in jetpack_options or jetpack_$name as appropriate.
|
194 |
+
*
|
195 |
+
* @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
|
196 |
+
* @param mixed $default (optional).
|
197 |
+
*
|
198 |
+
* @return mixed
|
199 |
+
*/
|
200 |
+
private static function get_option_from_database( $name, $default = false ) {
|
201 |
if ( self::is_valid( $name, 'non_compact' ) ) {
|
202 |
if ( self::is_network_option( $name ) ) {
|
203 |
return get_site_option( "jetpack_$name", $default );
|
504 |
|
505 |
/**
|
506 |
* This function checks for a constant that, if present, will disable direct DB queries Jetpack uses to manage certain options and force Jetpack to always use Options API instead.
|
507 |
+
* Options can be selectively managed via a blocklist by filtering option names via the jetpack_disabled_raw_option filter.
|
508 |
*
|
509 |
* @param string $name Option name.
|
510 |
*
|
520 |
*
|
521 |
* @since 5.5.0
|
522 |
*
|
523 |
+
* @param array $disabled_raw_options An array of option names that you can selectively blocklist from being managed via direct database queries.
|
524 |
*/
|
525 |
$disabled_raw_options = apply_filters( 'jetpack_disabled_raw_options', array() );
|
526 |
return isset( $disabled_raw_options[ $name ] );
|
vendor/automattic/jetpack-roles/phpunit.xml
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<phpunit bootstrap="tests/php/bootstrap.php" backupGlobals="false" colors="true">
|
2 |
+
<testsuites>
|
3 |
+
<testsuite name="main">
|
4 |
+
<directory prefix="test" suffix=".php">tests/php</directory>
|
5 |
+
</testsuite>
|
6 |
+
</testsuites>
|
7 |
+
</phpunit>
|
vendor/automattic/jetpack-status/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Jetpack Status
|
2 |
+
|
3 |
+
A status class for Jetpack.
|
4 |
+
|
5 |
+
Used to retrieve information about the current status of Jetpack and the site overall.
|
6 |
+
|
7 |
+
### Usage
|
8 |
+
|
9 |
+
Find out whether the site is in offline mode:
|
10 |
+
|
11 |
+
```php
|
12 |
+
use Automattic\Jetpack\Status;
|
13 |
+
|
14 |
+
$status = new Status();
|
15 |
+
$is_offline_mode = $status->is_offline_mode();
|
16 |
+
```
|
17 |
+
|
18 |
+
Find out whether this is a system with multiple networks:
|
19 |
+
|
20 |
+
```php
|
21 |
+
use Automattic\Jetpack\Status;
|
22 |
+
|
23 |
+
$status = new Status();
|
24 |
+
$is_multi_network = $status->is_multi_network();
|
25 |
+
```
|
26 |
+
|
27 |
+
Find out whether this site is a single user site:
|
28 |
+
|
29 |
+
```php
|
30 |
+
use Automattic\Jetpack\Status;
|
31 |
+
|
32 |
+
$status = new Status();
|
33 |
+
$is_single_user_site = $status->is_single_user_site();
|
34 |
+
```
|
vendor/automattic/jetpack-status/composer.json
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "automattic/jetpack-status",
|
3 |
+
"description": "Used to retrieve information about the current status of Jetpack and the site overall.",
|
4 |
+
"type": "library",
|
5 |
+
"license": "GPL-2.0-or-later",
|
6 |
+
"require": {},
|
7 |
+
"require-dev": {
|
8 |
+
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5",
|
9 |
+
"php-mock/php-mock": "^2.1",
|
10 |
+
"brain/monkey": "2.4.0"
|
11 |
+
},
|
12 |
+
"autoload": {
|
13 |
+
"classmap": [
|
14 |
+
"src/"
|
15 |
+
]
|
16 |
+
},
|
17 |
+
"scripts": {
|
18 |
+
"phpunit": [
|
19 |
+
"@composer install",
|
20 |
+
"./vendor/phpunit/phpunit/phpunit --colors=always"
|
21 |
+
]
|
22 |
+
},
|
23 |
+
"repositories": [
|
24 |
+
{
|
25 |
+
"type": "path",
|
26 |
+
"url": "../*"
|
27 |
+
}
|
28 |
+
],
|
29 |
+
"minimum-stability": "dev",
|
30 |
+
"prefer-stable": true
|
31 |
+
}
|
vendor/automattic/jetpack-status/src/class-status.php
ADDED
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* A status class for Jetpack.
|
4 |
+
*
|
5 |
+
* @package automattic/jetpack-status
|
6 |
+
*/
|
7 |
+
|
8 |
+
namespace Automattic\Jetpack;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class Automattic\Jetpack\Status
|
12 |
+
*
|
13 |
+
* Used to retrieve information about the current status of Jetpack and the site overall.
|
14 |
+
*/
|
15 |
+
class Status {
|
16 |
+
/**
|
17 |
+
* Is Jetpack in development (offline) mode?
|
18 |
+
*
|
19 |
+
* @deprecated 8.8.0 Use Status->is_offline_mode().
|
20 |
+
*
|
21 |
+
* @return bool Whether Jetpack's offline mode is active.
|
22 |
+
*/
|
23 |
+
public function is_development_mode() {
|
24 |
+
_deprecated_function( __FUNCTION__, 'Jetpack 8.8.0', 'Automattic\Jetpack\Status->is_offline_mode' );
|
25 |
+
return $this->is_offline_mode();
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Is Jetpack in offline mode?
|
30 |
+
*
|
31 |
+
* This was formerly called "Development Mode", but sites "in development" aren't always offline/localhost.
|
32 |
+
*
|
33 |
+
* @since 8.8.0
|
34 |
+
*
|
35 |
+
* @return bool Whether Jetpack's offline mode is active.
|
36 |
+
*/
|
37 |
+
public function is_offline_mode() {
|
38 |
+
$offline_mode = false;
|
39 |
+
|
40 |
+
if ( defined( '\\JETPACK_DEV_DEBUG' ) ) {
|
41 |
+
$offline_mode = constant( '\\JETPACK_DEV_DEBUG' );
|
42 |
+
} elseif ( defined( '\\WP_LOCAL_DEV' ) ) {
|
43 |
+
$offline_mode = constant( '\\WP_LOCAL_DEV' );
|
44 |
+
} elseif ( $this->is_local_site() ) {
|
45 |
+
$offline_mode = true;
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Filters Jetpack's offline mode.
|
50 |
+
*
|
51 |
+
* @see https://jetpack.com/support/development-mode/
|
52 |
+
* @todo Update documentation ^^.
|
53 |
+
*
|
54 |
+
* @since 2.2.1
|
55 |
+
* @deprecated 8.8.0
|
56 |
+
*
|
57 |
+
* @param bool $offline_mode Is Jetpack's offline mode active.
|
58 |
+
*/
|
59 |
+
$offline_mode = (bool) apply_filters_deprecated( 'jetpack_development_mode', array( $offline_mode ), '8.8.0', 'jetpack_offline_mode' );
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Filters Jetpack's offline mode.
|
63 |
+
*
|
64 |
+
* @see https://jetpack.com/support/development-mode/
|
65 |
+
* @todo Update documentation ^^.
|
66 |
+
*
|
67 |
+
* @since 8.8.0
|
68 |
+
*
|
69 |
+
* @param bool $offline_mode Is Jetpack's offline mode active.
|
70 |
+
*/
|
71 |
+
$offline_mode = (bool) apply_filters( 'jetpack_offline_mode', $offline_mode );
|
72 |
+
|
73 |
+
return $offline_mode;
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Whether this is a system with a multiple networks.
|
78 |
+
* Implemented since there is no core is_multi_network function.
|
79 |
+
* Right now there is no way to tell which network is the dominant network on the system.
|
80 |
+
*
|
81 |
+
* @return boolean
|
82 |
+
*/
|
83 |
+
public function is_multi_network() {
|
84 |
+
global $wpdb;
|
85 |
+
|
86 |
+
// If we don't have a multi site setup no need to do any more.
|
87 |
+
if ( ! is_multisite() ) {
|
88 |
+
return false;
|
89 |
+
}
|
90 |
+
|
91 |
+
$num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
|
92 |
+
if ( $num_sites > 1 ) {
|
93 |
+
return true;
|
94 |
+
}
|
95 |
+
|
96 |
+
return false;
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Whether the current site is single user site.
|
101 |
+
*
|
102 |
+
* @return bool
|
103 |
+
*/
|
104 |
+
public function is_single_user_site() {
|
105 |
+
global $wpdb;
|
106 |
+
|
107 |
+
$some_users = get_transient( 'jetpack_is_single_user' );
|
108 |
+
if ( false === $some_users ) {
|
109 |
+
$some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
|
110 |
+
set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
|
111 |
+
}
|
112 |
+
return 1 === (int) $some_users;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* If the site is a local site.
|
117 |
+
*
|
118 |
+
* @since 8.8.0
|
119 |
+
*
|
120 |
+
* @return bool
|
121 |
+
*/
|
122 |
+
public function is_local_site() {
|
123 |
+
// Check for localhost and sites using an IP only first.
|
124 |
+
$is_local = site_url() && false === strpos( site_url(), '.' );
|
125 |
+
|
126 |
+
// Then check for usual usual domains used by local dev tools.
|
127 |
+
$known_local = array(
|
128 |
+
'#\.local$#i',
|
129 |
+
'#\.localhost$#i',
|
130 |
+
'#\.test$#i',
|
131 |
+
'#\.docksal$#i', // Docksal.
|
132 |
+
'#\.docksal\.site$#i', // Docksal.
|
133 |
+
'#\.dev\.cc$#i', // ServerPress.
|
134 |
+
'#\.lndo\.site$#i', // Lando.
|
135 |
+
);
|
136 |
+
|
137 |
+
if ( ! $is_local ) {
|
138 |
+
foreach ( $known_local as $url ) {
|
139 |
+
if ( preg_match( $url, site_url() ) ) {
|
140 |
+
$is_local = true;
|
141 |
+
break;
|
142 |
+
}
|
143 |
+
}
|
144 |
+
}
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Filters is_local_site check.
|
148 |
+
*
|
149 |
+
* @since 8.8.0
|
150 |
+
*
|
151 |
+
* @param bool $is_local If the current site is a local site.
|
152 |
+
*/
|
153 |
+
return apply_filters( 'jetpack_is_local_site', $is_local );
|
154 |
+
}
|
155 |
+
|
156 |
+
/**
|
157 |
+
* If is a staging site.
|
158 |
+
*
|
159 |
+
* @todo Add IDC detection to a package.
|
160 |
+
*
|
161 |
+
* @return bool
|
162 |
+
*/
|
163 |
+
public function is_staging_site() {
|
164 |
+
// @todo Remove function_exists when WP 5.5 is the minimum version.
|
165 |
+
// Core's wp_get_environment_type allows for a few specific options. We should default to bowing out gracefully for anything other than production.
|
166 |
+
$is_staging = function_exists( 'wp_get_environment_type' ) && 'production' !== wp_get_environment_type();
|
167 |
+
|
168 |
+
$known_staging = array(
|
169 |
+
'urls' => array(
|
170 |
+
'#\.staging\.wpengine\.com$#i', // WP Engine.
|
171 |
+
'#\.staging\.kinsta\.com$#i', // Kinsta.com.
|
172 |
+
'#\.kinsta\.cloud$#i', // Kinsta.com.
|
173 |
+
'#\.stage\.site$#i', // DreamPress.
|
174 |
+
'#\.newspackstaging\.com$#i', // Newspack.
|
175 |
+
'#\.pantheonsite\.io$#i', // Pantheon.
|
176 |
+
'#\.flywheelsites\.com$#i', // Flywheel.
|
177 |
+
'#\.flywheelstaging\.com$#i', // Flywheel.
|
178 |
+
'#\.cloudwaysapps\.com$#i', // Cloudways.
|
179 |
+
'#\.azurewebsites\.net$#i', // Azure.
|
180 |
+
'#\.wpserveur\.net$#i', // WPServeur.
|
181 |
+
'#\-liquidwebsites\.com$#i', // Liquidweb.
|
182 |
+
),
|
183 |
+
'constants' => array(
|
184 |
+
'IS_WPE_SNAPSHOT', // WP Engine.
|
185 |
+
'KINSTA_DEV_ENV', // Kinsta.com.
|
186 |
+
'WPSTAGECOACH_STAGING', // WP Stagecoach.
|
187 |
+
'JETPACK_STAGING_MODE', // Generic.
|
188 |
+
'WP_LOCAL_DEV', // Generic.
|
189 |
+
),
|
190 |
+
);
|
191 |
+
/**
|
192 |
+
* Filters the flags of known staging sites.
|
193 |
+
*
|
194 |
+
* @since 3.9.0
|
195 |
+
*
|
196 |
+
* @param array $known_staging {
|
197 |
+
* An array of arrays that each are used to check if the current site is staging.
|
198 |
+
* @type array $urls URLs of staging sites in regex to check against site_url.
|
199 |
+
* @type array $constants PHP constants of known staging/developement environments.
|
200 |
+
* }
|
201 |
+
*/
|
202 |
+
$known_staging = apply_filters( 'jetpack_known_staging', $known_staging );
|
203 |
+
|
204 |
+
if ( isset( $known_staging['urls'] ) ) {
|
205 |
+
foreach ( $known_staging['urls'] as $url ) {
|
206 |
+
if ( preg_match( $url, site_url() ) ) {
|
207 |
+
$is_staging = true;
|
208 |
+
break;
|
209 |
+
}
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
if ( isset( $known_staging['constants'] ) ) {
|
214 |
+
foreach ( $known_staging['constants'] as $constant ) {
|
215 |
+
if ( defined( $constant ) && constant( $constant ) ) {
|
216 |
+
$is_staging = true;
|
217 |
+
}
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
// Last, let's check if sync is erroring due to an IDC. If so, set the site to staging mode.
|
222 |
+
if ( ! $is_staging && method_exists( 'Jetpack', 'validate_sync_error_idc_option' ) && \Jetpack::validate_sync_error_idc_option() ) {
|
223 |
+
$is_staging = true;
|
224 |
+
}
|
225 |
+
|
226 |
+
/**
|
227 |
+
* Filters is_staging_site check.
|
228 |
+
*
|
229 |
+
* @since 3.9.0
|
230 |
+
*
|
231 |
+
* @param bool $is_staging If the current site is a staging site.
|
232 |
+
*/
|
233 |
+
return apply_filters( 'jetpack_is_staging_site', $is_staging );
|
234 |
+
}
|
235 |
+
}
|
vendor/composer/ClassLoader.php
CHANGED
@@ -279,7 +279,7 @@ class ClassLoader
|
|
279 |
*/
|
280 |
public function setApcuPrefix($apcuPrefix)
|
281 |
{
|
282 |
-
$this->apcuPrefix = function_exists('apcu_fetch') &&
|
283 |
}
|
284 |
|
285 |
/**
|
279 |
*/
|
280 |
public function setApcuPrefix($apcuPrefix)
|
281 |
{
|
282 |
+
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
283 |
}
|
284 |
|
285 |
/**
|
vendor/composer/autoload_classmap.php
CHANGED
@@ -8,6 +8,8 @@ $baseDir = dirname($vendorDir);
|
|
8 |
return array(
|
9 |
'Automattic\\Jetpack\\Config' => $vendorDir . '/automattic/jetpack-config/src/class-config.php',
|
10 |
'Automattic\\Jetpack\\Connection\\Client' => $vendorDir . '/automattic/jetpack-connection/src/class-client.php',
|
|
|
|
|
11 |
'Automattic\\Jetpack\\Connection\\Manager' => $vendorDir . '/automattic/jetpack-connection/src/class-manager.php',
|
12 |
'Automattic\\Jetpack\\Connection\\Manager_Interface' => $vendorDir . '/automattic/jetpack-connection/src/interface-manager.php',
|
13 |
'Automattic\\Jetpack\\Connection\\Plugin' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin.php',
|
@@ -17,6 +19,7 @@ return array(
|
|
17 |
'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => $vendorDir . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php',
|
18 |
'Automattic\\Jetpack\\Constants' => $vendorDir . '/automattic/jetpack-constants/src/class-constants.php',
|
19 |
'Automattic\\Jetpack\\Roles' => $vendorDir . '/automattic/jetpack-roles/src/class-roles.php',
|
|
|
20 |
'Jetpack_IXR_Client' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php',
|
21 |
'Jetpack_IXR_ClientMulticall' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php',
|
22 |
'Jetpack_Options' => $vendorDir . '/automattic/jetpack-options/legacy/class-jetpack-options.php',
|
8 |
return array(
|
9 |
'Automattic\\Jetpack\\Config' => $vendorDir . '/automattic/jetpack-config/src/class-config.php',
|
10 |
'Automattic\\Jetpack\\Connection\\Client' => $vendorDir . '/automattic/jetpack-connection/src/class-client.php',
|
11 |
+
'Automattic\\Jetpack\\Connection\\Error_Handler' => $vendorDir . '/automattic/jetpack-connection/src/class-error-handler.php',
|
12 |
+
'Automattic\\Jetpack\\Connection\\Error_Handlers\\Invalid_Blog_Token' => $vendorDir . '/automattic/jetpack-connection/src/error-handlers/class-invalid-blog-token.php',
|
13 |
'Automattic\\Jetpack\\Connection\\Manager' => $vendorDir . '/automattic/jetpack-connection/src/class-manager.php',
|
14 |
'Automattic\\Jetpack\\Connection\\Manager_Interface' => $vendorDir . '/automattic/jetpack-connection/src/interface-manager.php',
|
15 |
'Automattic\\Jetpack\\Connection\\Plugin' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin.php',
|
19 |
'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => $vendorDir . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php',
|
20 |
'Automattic\\Jetpack\\Constants' => $vendorDir . '/automattic/jetpack-constants/src/class-constants.php',
|
21 |
'Automattic\\Jetpack\\Roles' => $vendorDir . '/automattic/jetpack-roles/src/class-roles.php',
|
22 |
+
'Automattic\\Jetpack\\Status' => $vendorDir . '/automattic/jetpack-status/src/class-status.php',
|
23 |
'Jetpack_IXR_Client' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php',
|
24 |
'Jetpack_IXR_ClientMulticall' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php',
|
25 |
'Jetpack_Options' => $vendorDir . '/automattic/jetpack-options/legacy/class-jetpack-options.php',
|
vendor/composer/autoload_classmap_package.php
CHANGED
@@ -15,67 +15,79 @@ return array(
|
|
15 |
'path' => $vendorDir . '/automattic/jetpack-autoloader/src/CustomAutoloaderPlugin.php'
|
16 |
),
|
17 |
'Jetpack_XMLRPC_Server' => array(
|
18 |
-
'version' => '1.
|
19 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php'
|
20 |
),
|
21 |
'Jetpack_IXR_Client' => array(
|
22 |
-
'version' => '1.
|
23 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php'
|
24 |
),
|
25 |
'Jetpack_Signature' => array(
|
26 |
-
'version' => '1.
|
27 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-signature.php'
|
28 |
),
|
29 |
'Jetpack_IXR_ClientMulticall' => array(
|
30 |
-
'version' => '1.
|
31 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php'
|
32 |
),
|
|
|
|
|
|
|
|
|
33 |
'Automattic\\Jetpack\\Connection\\REST_Connector' => array(
|
34 |
-
'version' => '1.
|
35 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-rest-connector.php'
|
36 |
),
|
37 |
'Automattic\\Jetpack\\Connection\\Plugin_Storage' => array(
|
38 |
-
'version' => '1.
|
39 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin-storage.php'
|
40 |
),
|
41 |
'Automattic\\Jetpack\\Connection\\Client' => array(
|
42 |
-
'version' => '1.
|
43 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-client.php'
|
44 |
),
|
45 |
'Automattic\\Jetpack\\Connection\\Utils' => array(
|
46 |
-
'version' => '1.
|
47 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-utils.php'
|
48 |
),
|
49 |
'Automattic\\Jetpack\\Connection\\Manager_Interface' => array(
|
50 |
-
'version' => '1.
|
51 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/interface-manager.php'
|
52 |
),
|
53 |
'Automattic\\Jetpack\\Connection\\Plugin' => array(
|
54 |
-
'version' => '1.
|
55 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin.php'
|
56 |
),
|
57 |
'Automattic\\Jetpack\\Connection\\Manager' => array(
|
58 |
-
'version' => '1.
|
59 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-manager.php'
|
60 |
),
|
61 |
'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => array(
|
62 |
-
'version' => '1.
|
63 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php'
|
64 |
),
|
|
|
|
|
|
|
|
|
65 |
'Automattic\\Jetpack\\Config' => array(
|
66 |
-
'version' => '1.
|
67 |
'path' => $vendorDir . '/automattic/jetpack-config/src/class-config.php'
|
68 |
),
|
69 |
'Jetpack_Options' => array(
|
70 |
-
'version' => '1.
|
71 |
'path' => $vendorDir . '/automattic/jetpack-options/legacy/class-jetpack-options.php'
|
72 |
),
|
73 |
'Automattic\\Jetpack\\Roles' => array(
|
74 |
-
'version' => '1.0.
|
75 |
'path' => $vendorDir . '/automattic/jetpack-roles/src/class-roles.php'
|
76 |
),
|
|
|
|
|
|
|
|
|
77 |
'Automattic\\Jetpack\\Constants' => array(
|
78 |
-
'version' => '1.
|
79 |
'path' => $vendorDir . '/automattic/jetpack-constants/src/class-constants.php'
|
80 |
),
|
81 |
);
|
15 |
'path' => $vendorDir . '/automattic/jetpack-autoloader/src/CustomAutoloaderPlugin.php'
|
16 |
),
|
17 |
'Jetpack_XMLRPC_Server' => array(
|
18 |
+
'version' => '1.15.2.0',
|
19 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php'
|
20 |
),
|
21 |
'Jetpack_IXR_Client' => array(
|
22 |
+
'version' => '1.15.2.0',
|
23 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php'
|
24 |
),
|
25 |
'Jetpack_Signature' => array(
|
26 |
+
'version' => '1.15.2.0',
|
27 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-signature.php'
|
28 |
),
|
29 |
'Jetpack_IXR_ClientMulticall' => array(
|
30 |
+
'version' => '1.15.2.0',
|
31 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php'
|
32 |
),
|
33 |
+
'Automattic\\Jetpack\\Connection\\Error_Handler' => array(
|
34 |
+
'version' => '1.15.2.0',
|
35 |
+
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-error-handler.php'
|
36 |
+
),
|
37 |
'Automattic\\Jetpack\\Connection\\REST_Connector' => array(
|
38 |
+
'version' => '1.15.2.0',
|
39 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-rest-connector.php'
|
40 |
),
|
41 |
'Automattic\\Jetpack\\Connection\\Plugin_Storage' => array(
|
42 |
+
'version' => '1.15.2.0',
|
43 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin-storage.php'
|
44 |
),
|
45 |
'Automattic\\Jetpack\\Connection\\Client' => array(
|
46 |
+
'version' => '1.15.2.0',
|
47 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-client.php'
|
48 |
),
|
49 |
'Automattic\\Jetpack\\Connection\\Utils' => array(
|
50 |
+
'version' => '1.15.2.0',
|
51 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-utils.php'
|
52 |
),
|
53 |
'Automattic\\Jetpack\\Connection\\Manager_Interface' => array(
|
54 |
+
'version' => '1.15.2.0',
|
55 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/interface-manager.php'
|
56 |
),
|
57 |
'Automattic\\Jetpack\\Connection\\Plugin' => array(
|
58 |
+
'version' => '1.15.2.0',
|
59 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin.php'
|
60 |
),
|
61 |
'Automattic\\Jetpack\\Connection\\Manager' => array(
|
62 |
+
'version' => '1.15.2.0',
|
63 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-manager.php'
|
64 |
),
|
65 |
'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => array(
|
66 |
+
'version' => '1.15.2.0',
|
67 |
'path' => $vendorDir . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php'
|
68 |
),
|
69 |
+
'Automattic\\Jetpack\\Connection\\Error_Handlers\\Invalid_Blog_Token' => array(
|
70 |
+
'version' => '1.15.2.0',
|
71 |
+
'path' => $vendorDir . '/automattic/jetpack-connection/src/error-handlers/class-invalid-blog-token.php'
|
72 |
+
),
|
73 |
'Automattic\\Jetpack\\Config' => array(
|
74 |
+
'version' => '1.3.0.0',
|
75 |
'path' => $vendorDir . '/automattic/jetpack-config/src/class-config.php'
|
76 |
),
|
77 |
'Jetpack_Options' => array(
|
78 |
+
'version' => '1.7.0.0',
|
79 |
'path' => $vendorDir . '/automattic/jetpack-options/legacy/class-jetpack-options.php'
|
80 |
),
|
81 |
'Automattic\\Jetpack\\Roles' => array(
|
82 |
+
'version' => '1.2.0.0',
|
83 |
'path' => $vendorDir . '/automattic/jetpack-roles/src/class-roles.php'
|
84 |
),
|
85 |
+
'Automattic\\Jetpack\\Status' => array(
|
86 |
+
'version' => '1.3.0.0',
|
87 |
+
'path' => $vendorDir . '/automattic/jetpack-status/src/class-status.php'
|
88 |
+
),
|
89 |
'Automattic\\Jetpack\\Constants' => array(
|
90 |
+
'version' => '1.4.0.0',
|
91 |
'path' => $vendorDir . '/automattic/jetpack-constants/src/class-constants.php'
|
92 |
),
|
93 |
);
|
vendor/composer/autoload_files_package.php
CHANGED
@@ -7,7 +7,7 @@ $baseDir = dirname($vendorDir);
|
|
7 |
|
8 |
return array(
|
9 |
'bce4ecd6aabb2a2948e06d0e2c4ea9a6' => array(
|
10 |
-
'version' => '1.
|
11 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/load-ixr.php'
|
12 |
),
|
13 |
);
|
7 |
|
8 |
return array(
|
9 |
'bce4ecd6aabb2a2948e06d0e2c4ea9a6' => array(
|
10 |
+
'version' => '1.15.2.0',
|
11 |
'path' => $vendorDir . '/automattic/jetpack-connection/legacy/load-ixr.php'
|
12 |
),
|
13 |
);
|
vendor/composer/autoload_real.php
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
// autoload_real.php @generated by Composer
|
4 |
|
5 |
-
class
|
6 |
{
|
7 |
private static $loader;
|
8 |
|
@@ -13,24 +13,21 @@ class ComposerAutoloaderInita2447e0d68af485c4e75e9838c0a2df4
|
|
13 |
}
|
14 |
}
|
15 |
|
16 |
-
/**
|
17 |
-
* @return \Composer\Autoload\ClassLoader
|
18 |
-
*/
|
19 |
public static function getLoader()
|
20 |
{
|
21 |
if (null !== self::$loader) {
|
22 |
return self::$loader;
|
23 |
}
|
24 |
|
25 |
-
spl_autoload_register(array('
|
26 |
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
27 |
-
spl_autoload_unregister(array('
|
28 |
|
29 |
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
30 |
if ($useStaticLoader) {
|
31 |
require_once __DIR__ . '/autoload_static.php';
|
32 |
|
33 |
-
call_user_func(\Composer\Autoload\
|
34 |
} else {
|
35 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
36 |
foreach ($map as $namespace => $path) {
|
@@ -51,19 +48,19 @@ class ComposerAutoloaderInita2447e0d68af485c4e75e9838c0a2df4
|
|
51 |
$loader->register(true);
|
52 |
|
53 |
if ($useStaticLoader) {
|
54 |
-
$includeFiles = Composer\Autoload\
|
55 |
} else {
|
56 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
57 |
}
|
58 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
59 |
-
|
60 |
}
|
61 |
|
62 |
return $loader;
|
63 |
}
|
64 |
}
|
65 |
|
66 |
-
function
|
67 |
{
|
68 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
69 |
require $file;
|
2 |
|
3 |
// autoload_real.php @generated by Composer
|
4 |
|
5 |
+
class ComposerAutoloaderInit0a651f92f6dec0685e6f92fb5fc9b1f9
|
6 |
{
|
7 |
private static $loader;
|
8 |
|
13 |
}
|
14 |
}
|
15 |
|
|
|
|
|
|
|
16 |
public static function getLoader()
|
17 |
{
|
18 |
if (null !== self::$loader) {
|
19 |
return self::$loader;
|
20 |
}
|
21 |
|
22 |
+
spl_autoload_register(array('ComposerAutoloaderInit0a651f92f6dec0685e6f92fb5fc9b1f9', 'loadClassLoader'), true, true);
|
23 |
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
24 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit0a651f92f6dec0685e6f92fb5fc9b1f9', 'loadClassLoader'));
|
25 |
|
26 |
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
27 |
if ($useStaticLoader) {
|
28 |
require_once __DIR__ . '/autoload_static.php';
|
29 |
|
30 |
+
call_user_func(\Composer\Autoload\ComposerStaticInit0a651f92f6dec0685e6f92fb5fc9b1f9::getInitializer($loader));
|
31 |
} else {
|
32 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
33 |
foreach ($map as $namespace => $path) {
|
48 |
$loader->register(true);
|
49 |
|
50 |
if ($useStaticLoader) {
|
51 |
+
$includeFiles = Composer\Autoload\ComposerStaticInit0a651f92f6dec0685e6f92fb5fc9b1f9::$files;
|
52 |
} else {
|
53 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
54 |
}
|
55 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
56 |
+
composerRequire0a651f92f6dec0685e6f92fb5fc9b1f9($fileIdentifier, $file);
|
57 |
}
|
58 |
|
59 |
return $loader;
|
60 |
}
|
61 |
}
|
62 |
|
63 |
+
function composerRequire0a651f92f6dec0685e6f92fb5fc9b1f9($fileIdentifier, $file)
|
64 |
{
|
65 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
66 |
require $file;
|
vendor/composer/autoload_static.php
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
|
5 |
namespace Composer\Autoload;
|
6 |
|
7 |
-
class
|
8 |
{
|
9 |
public static $files = array (
|
10 |
'bce4ecd6aabb2a2948e06d0e2c4ea9a6' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/load-ixr.php',
|
@@ -27,6 +27,8 @@ class ComposerStaticInita2447e0d68af485c4e75e9838c0a2df4
|
|
27 |
public static $classMap = array (
|
28 |
'Automattic\\Jetpack\\Config' => __DIR__ . '/..' . '/automattic/jetpack-config/src/class-config.php',
|
29 |
'Automattic\\Jetpack\\Connection\\Client' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-client.php',
|
|
|
|
|
30 |
'Automattic\\Jetpack\\Connection\\Manager' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-manager.php',
|
31 |
'Automattic\\Jetpack\\Connection\\Manager_Interface' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/interface-manager.php',
|
32 |
'Automattic\\Jetpack\\Connection\\Plugin' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-plugin.php',
|
@@ -36,6 +38,7 @@ class ComposerStaticInita2447e0d68af485c4e75e9838c0a2df4
|
|
36 |
'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php',
|
37 |
'Automattic\\Jetpack\\Constants' => __DIR__ . '/..' . '/automattic/jetpack-constants/src/class-constants.php',
|
38 |
'Automattic\\Jetpack\\Roles' => __DIR__ . '/..' . '/automattic/jetpack-roles/src/class-roles.php',
|
|
|
39 |
'Jetpack_IXR_Client' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php',
|
40 |
'Jetpack_IXR_ClientMulticall' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php',
|
41 |
'Jetpack_Options' => __DIR__ . '/..' . '/automattic/jetpack-options/legacy/class-jetpack-options.php',
|
@@ -46,9 +49,9 @@ class ComposerStaticInita2447e0d68af485c4e75e9838c0a2df4
|
|
46 |
public static function getInitializer(ClassLoader $loader)
|
47 |
{
|
48 |
return \Closure::bind(function () use ($loader) {
|
49 |
-
$loader->prefixLengthsPsr4 =
|
50 |
-
$loader->prefixDirsPsr4 =
|
51 |
-
$loader->classMap =
|
52 |
|
53 |
}, null, ClassLoader::class);
|
54 |
}
|
4 |
|
5 |
namespace Composer\Autoload;
|
6 |
|
7 |
+
class ComposerStaticInit0a651f92f6dec0685e6f92fb5fc9b1f9
|
8 |
{
|
9 |
public static $files = array (
|
10 |
'bce4ecd6aabb2a2948e06d0e2c4ea9a6' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/load-ixr.php',
|
27 |
public static $classMap = array (
|
28 |
'Automattic\\Jetpack\\Config' => __DIR__ . '/..' . '/automattic/jetpack-config/src/class-config.php',
|
29 |
'Automattic\\Jetpack\\Connection\\Client' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-client.php',
|
30 |
+
'Automattic\\Jetpack\\Connection\\Error_Handler' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-error-handler.php',
|
31 |
+
'Automattic\\Jetpack\\Connection\\Error_Handlers\\Invalid_Blog_Token' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/error-handlers/class-invalid-blog-token.php',
|
32 |
'Automattic\\Jetpack\\Connection\\Manager' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-manager.php',
|
33 |
'Automattic\\Jetpack\\Connection\\Manager_Interface' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/interface-manager.php',
|
34 |
'Automattic\\Jetpack\\Connection\\Plugin' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-plugin.php',
|
38 |
'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php',
|
39 |
'Automattic\\Jetpack\\Constants' => __DIR__ . '/..' . '/automattic/jetpack-constants/src/class-constants.php',
|
40 |
'Automattic\\Jetpack\\Roles' => __DIR__ . '/..' . '/automattic/jetpack-roles/src/class-roles.php',
|
41 |
+
'Automattic\\Jetpack\\Status' => __DIR__ . '/..' . '/automattic/jetpack-status/src/class-status.php',
|
42 |
'Jetpack_IXR_Client' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php',
|
43 |
'Jetpack_IXR_ClientMulticall' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php',
|
44 |
'Jetpack_Options' => __DIR__ . '/..' . '/automattic/jetpack-options/legacy/class-jetpack-options.php',
|
49 |
public static function getInitializer(ClassLoader $loader)
|
50 |
{
|
51 |
return \Closure::bind(function () use ($loader) {
|
52 |
+
$loader->prefixLengthsPsr4 = ComposerStaticInit0a651f92f6dec0685e6f92fb5fc9b1f9::$prefixLengthsPsr4;
|
53 |
+
$loader->prefixDirsPsr4 = ComposerStaticInit0a651f92f6dec0685e6f92fb5fc9b1f9::$prefixDirsPsr4;
|
54 |
+
$loader->classMap = ComposerStaticInit0a651f92f6dec0685e6f92fb5fc9b1f9::$classMap;
|
55 |
|
56 |
}, null, ClassLoader::class);
|
57 |
}
|
vendor/composer/installed.json
CHANGED
@@ -39,20 +39,20 @@
|
|
39 |
},
|
40 |
{
|
41 |
"name": "automattic/jetpack-config",
|
42 |
-
"version": "v1.
|
43 |
-
"version_normalized": "1.
|
44 |
"source": {
|
45 |
"type": "git",
|
46 |
"url": "https://github.com/Automattic/jetpack-config.git",
|
47 |
-
"reference": "
|
48 |
},
|
49 |
"dist": {
|
50 |
"type": "zip",
|
51 |
-
"url": "https://api.github.com/repos/Automattic/jetpack-config/zipball/
|
52 |
-
"reference": "
|
53 |
"shasum": ""
|
54 |
},
|
55 |
-
"time": "2020-
|
56 |
"type": "library",
|
57 |
"installation-source": "dist",
|
58 |
"autoload": {
|
@@ -68,29 +68,31 @@
|
|
68 |
},
|
69 |
{
|
70 |
"name": "automattic/jetpack-connection",
|
71 |
-
"version": "v1.
|
72 |
-
"version_normalized": "1.
|
73 |
"source": {
|
74 |
"type": "git",
|
75 |
"url": "https://github.com/Automattic/jetpack-connection.git",
|
76 |
-
"reference": "
|
77 |
},
|
78 |
"dist": {
|
79 |
"type": "zip",
|
80 |
-
"url": "https://api.github.com/repos/Automattic/jetpack-connection/zipball/
|
81 |
-
"reference": "
|
82 |
"shasum": ""
|
83 |
},
|
84 |
"require": {
|
85 |
-
"automattic/jetpack-constants": "1.
|
86 |
-
"automattic/jetpack-options": "1.
|
87 |
-
"automattic/jetpack-roles": "1.0
|
|
|
88 |
},
|
89 |
"require-dev": {
|
|
|
90 |
"php-mock/php-mock": "^2.1",
|
91 |
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
|
92 |
},
|
93 |
-
"time": "2020-
|
94 |
"type": "library",
|
95 |
"installation-source": "dist",
|
96 |
"autoload": {
|
@@ -110,24 +112,24 @@
|
|
110 |
},
|
111 |
{
|
112 |
"name": "automattic/jetpack-constants",
|
113 |
-
"version": "v1.
|
114 |
-
"version_normalized": "1.
|
115 |
"source": {
|
116 |
"type": "git",
|
117 |
"url": "https://github.com/Automattic/jetpack-constants.git",
|
118 |
-
"reference": "
|
119 |
},
|
120 |
"dist": {
|
121 |
"type": "zip",
|
122 |
-
"url": "https://api.github.com/repos/Automattic/jetpack-constants/zipball/
|
123 |
-
"reference": "
|
124 |
"shasum": ""
|
125 |
},
|
126 |
"require-dev": {
|
127 |
"php-mock/php-mock": "^2.1",
|
128 |
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
|
129 |
},
|
130 |
-
"time": "2020-
|
131 |
"type": "library",
|
132 |
"installation-source": "dist",
|
133 |
"autoload": {
|
@@ -143,27 +145,27 @@
|
|
143 |
},
|
144 |
{
|
145 |
"name": "automattic/jetpack-options",
|
146 |
-
"version": "v1.
|
147 |
-
"version_normalized": "1.
|
148 |
"source": {
|
149 |
"type": "git",
|
150 |
"url": "https://github.com/Automattic/jetpack-options.git",
|
151 |
-
"reference": "
|
152 |
},
|
153 |
"dist": {
|
154 |
"type": "zip",
|
155 |
-
"url": "https://api.github.com/repos/Automattic/jetpack-options/zipball/
|
156 |
-
"reference": "
|
157 |
"shasum": ""
|
158 |
},
|
159 |
"require": {
|
160 |
-
"automattic/jetpack-constants": "1.
|
161 |
},
|
162 |
"require-dev": {
|
163 |
"10up/wp_mock": "0.4.2",
|
164 |
"phpunit/phpunit": "7.*.*"
|
165 |
},
|
166 |
-
"time": "2020-
|
167 |
"type": "library",
|
168 |
"installation-source": "dist",
|
169 |
"autoload": {
|
@@ -179,24 +181,24 @@
|
|
179 |
},
|
180 |
{
|
181 |
"name": "automattic/jetpack-roles",
|
182 |
-
"version": "v1.0
|
183 |
-
"version_normalized": "1.0.
|
184 |
"source": {
|
185 |
"type": "git",
|
186 |
"url": "https://github.com/Automattic/jetpack-roles.git",
|
187 |
-
"reference": "
|
188 |
},
|
189 |
"dist": {
|
190 |
"type": "zip",
|
191 |
-
"url": "https://api.github.com/repos/Automattic/jetpack-roles/zipball/
|
192 |
-
"reference": "
|
193 |
"shasum": ""
|
194 |
},
|
195 |
"require-dev": {
|
196 |
"php-mock/php-mock": "^2.1",
|
197 |
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
|
198 |
},
|
199 |
-
"time": "
|
200 |
"type": "library",
|
201 |
"installation-source": "dist",
|
202 |
"autoload": {
|
@@ -209,5 +211,39 @@
|
|
209 |
"GPL-2.0-or-later"
|
210 |
],
|
211 |
"description": "Utilities, related with user roles and capabilities."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
}
|
213 |
]
|
39 |
},
|
40 |
{
|
41 |
"name": "automattic/jetpack-config",
|
42 |
+
"version": "v1.3.0",
|
43 |
+
"version_normalized": "1.3.0.0",
|
44 |
"source": {
|
45 |
"type": "git",
|
46 |
"url": "https://github.com/Automattic/jetpack-config.git",
|
47 |
+
"reference": "a7ae4b6f32c964fb7ea0f715aa5ba272a1f06113"
|
48 |
},
|
49 |
"dist": {
|
50 |
"type": "zip",
|
51 |
+
"url": "https://api.github.com/repos/Automattic/jetpack-config/zipball/a7ae4b6f32c964fb7ea0f715aa5ba272a1f06113",
|
52 |
+
"reference": "a7ae4b6f32c964fb7ea0f715aa5ba272a1f06113",
|
53 |
"shasum": ""
|
54 |
},
|
55 |
+
"time": "2020-06-26T07:31:13+00:00",
|
56 |
"type": "library",
|
57 |
"installation-source": "dist",
|
58 |
"autoload": {
|
68 |
},
|
69 |
{
|
70 |
"name": "automattic/jetpack-connection",
|
71 |
+
"version": "v1.15.2",
|
72 |
+
"version_normalized": "1.15.2.0",
|
73 |
"source": {
|
74 |
"type": "git",
|
75 |
"url": "https://github.com/Automattic/jetpack-connection.git",
|
76 |
+
"reference": "eff65e640bcec826962475e87dcb236741a2a762"
|
77 |
},
|
78 |
"dist": {
|
79 |
"type": "zip",
|
80 |
+
"url": "https://api.github.com/repos/Automattic/jetpack-connection/zipball/eff65e640bcec826962475e87dcb236741a2a762",
|
81 |
+
"reference": "eff65e640bcec826962475e87dcb236741a2a762",
|
82 |
"shasum": ""
|
83 |
},
|
84 |
"require": {
|
85 |
+
"automattic/jetpack-constants": "1.4.0",
|
86 |
+
"automattic/jetpack-options": "1.7.0",
|
87 |
+
"automattic/jetpack-roles": "1.2.0",
|
88 |
+
"automattic/jetpack-status": "1.3.0"
|
89 |
},
|
90 |
"require-dev": {
|
91 |
+
"automattic/wordbless": "@dev",
|
92 |
"php-mock/php-mock": "^2.1",
|
93 |
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
|
94 |
},
|
95 |
+
"time": "2020-08-10T15:51:57+00:00",
|
96 |
"type": "library",
|
97 |
"installation-source": "dist",
|
98 |
"autoload": {
|
112 |
},
|
113 |
{
|
114 |
"name": "automattic/jetpack-constants",
|
115 |
+
"version": "v1.4.0",
|
116 |
+
"version_normalized": "1.4.0.0",
|
117 |
"source": {
|
118 |
"type": "git",
|
119 |
"url": "https://github.com/Automattic/jetpack-constants.git",
|
120 |
+
"reference": "b4210d56948529b43785ce31e0055f435eac1f9f"
|
121 |
},
|
122 |
"dist": {
|
123 |
"type": "zip",
|
124 |
+
"url": "https://api.github.com/repos/Automattic/jetpack-constants/zipball/b4210d56948529b43785ce31e0055f435eac1f9f",
|
125 |
+
"reference": "b4210d56948529b43785ce31e0055f435eac1f9f",
|
126 |
"shasum": ""
|
127 |
},
|
128 |
"require-dev": {
|
129 |
"php-mock/php-mock": "^2.1",
|
130 |
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
|
131 |
},
|
132 |
+
"time": "2020-07-01T15:55:35+00:00",
|
133 |
"type": "library",
|
134 |
"installation-source": "dist",
|
135 |
"autoload": {
|
145 |
},
|
146 |
{
|
147 |
"name": "automattic/jetpack-options",
|
148 |
+
"version": "v1.7.0",
|
149 |
+
"version_normalized": "1.7.0.0",
|
150 |
"source": {
|
151 |
"type": "git",
|
152 |
"url": "https://github.com/Automattic/jetpack-options.git",
|
153 |
+
"reference": "8006d330476d93a1cb768f30a875f0b17d16f7f6"
|
154 |
},
|
155 |
"dist": {
|
156 |
"type": "zip",
|
157 |
+
"url": "https://api.github.com/repos/Automattic/jetpack-options/zipball/8006d330476d93a1cb768f30a875f0b17d16f7f6",
|
158 |
+
"reference": "8006d330476d93a1cb768f30a875f0b17d16f7f6",
|
159 |
"shasum": ""
|
160 |
},
|
161 |
"require": {
|
162 |
+
"automattic/jetpack-constants": "1.4.0"
|
163 |
},
|
164 |
"require-dev": {
|
165 |
"10up/wp_mock": "0.4.2",
|
166 |
"phpunit/phpunit": "7.*.*"
|
167 |
},
|
168 |
+
"time": "2020-07-28T14:03:34+00:00",
|
169 |
"type": "library",
|
170 |
"installation-source": "dist",
|
171 |
"autoload": {
|
181 |
},
|
182 |
{
|
183 |
"name": "automattic/jetpack-roles",
|
184 |
+
"version": "v1.2.0",
|
185 |
+
"version_normalized": "1.2.0.0",
|
186 |
"source": {
|
187 |
"type": "git",
|
188 |
"url": "https://github.com/Automattic/jetpack-roles.git",
|
189 |
+
"reference": "0148108451db7ee5bfb68669671fc50ddb6d1474"
|
190 |
},
|
191 |
"dist": {
|
192 |
"type": "zip",
|
193 |
+
"url": "https://api.github.com/repos/Automattic/jetpack-roles/zipball/0148108451db7ee5bfb68669671fc50ddb6d1474",
|
194 |
+
"reference": "0148108451db7ee5bfb68669671fc50ddb6d1474",
|
195 |
"shasum": ""
|
196 |
},
|
197 |
"require-dev": {
|
198 |
"php-mock/php-mock": "^2.1",
|
199 |
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
|
200 |
},
|
201 |
+
"time": "2020-07-01T15:55:45+00:00",
|
202 |
"type": "library",
|
203 |
"installation-source": "dist",
|
204 |
"autoload": {
|
211 |
"GPL-2.0-or-later"
|
212 |
],
|
213 |
"description": "Utilities, related with user roles and capabilities."
|
214 |
+
},
|
215 |
+
{
|
216 |
+
"name": "automattic/jetpack-status",
|
217 |
+
"version": "v1.3.0",
|
218 |
+
"version_normalized": "1.3.0.0",
|
219 |
+
"source": {
|
220 |
+
"type": "git",
|
221 |
+
"url": "https://github.com/Automattic/jetpack-status.git",
|
222 |
+
"reference": "09bd04d677832f348d3b9d520eecd856c2f0cafb"
|
223 |
+
},
|
224 |
+
"dist": {
|
225 |
+
"type": "zip",
|
226 |
+
"url": "https://api.github.com/repos/Automattic/jetpack-status/zipball/09bd04d677832f348d3b9d520eecd856c2f0cafb",
|
227 |
+
"reference": "09bd04d677832f348d3b9d520eecd856c2f0cafb",
|
228 |
+
"shasum": ""
|
229 |
+
},
|
230 |
+
"require-dev": {
|
231 |
+
"brain/monkey": "2.4.0",
|
232 |
+
"php-mock/php-mock": "^2.1",
|
233 |
+
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
|
234 |
+
},
|
235 |
+
"time": "2020-07-28T08:21:54+00:00",
|
236 |
+
"type": "library",
|
237 |
+
"installation-source": "dist",
|
238 |
+
"autoload": {
|
239 |
+
"classmap": [
|
240 |
+
"src/"
|
241 |
+
]
|
242 |
+
},
|
243 |
+
"notification-url": "https://packagist.org/downloads/",
|
244 |
+
"license": [
|
245 |
+
"GPL-2.0-or-later"
|
246 |
+
],
|
247 |
+
"description": "Used to retrieve information about the current status of Jetpack and the site overall."
|
248 |
}
|
249 |
]
|
woocommerce-payments.php
CHANGED
@@ -8,9 +8,9 @@
|
|
8 |
* Text Domain: woocommerce-payments
|
9 |
* Domain Path: /languages
|
10 |
* WC requires at least: 4.0
|
11 |
-
* WC tested up to: 4.
|
12 |
* Requires WP: 5.3
|
13 |
-
* Version: 1.
|
14 |
*
|
15 |
* @package WooCommerce\Payments
|
16 |
*/
|
@@ -29,6 +29,9 @@ require_once WCPAY_ABSPATH . 'vendor/autoload_packages.php';
|
|
29 |
* Initialize the Jetpack connection functionality.
|
30 |
*/
|
31 |
function wcpay_jetpack_init() {
|
|
|
|
|
|
|
32 |
$jetpack_config = new Automattic\Jetpack\Config();
|
33 |
$jetpack_config->ensure(
|
34 |
'connection',
|
@@ -52,4 +55,34 @@ function wcpay_init() {
|
|
52 |
}
|
53 |
|
54 |
// Make sure this is run *after* WooCommerce has a chance to initialize its packages (wc-admin, etc). That is run with priority 10.
|
|
|
55 |
add_action( 'plugins_loaded', 'wcpay_init', 11 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
* Text Domain: woocommerce-payments
|
9 |
* Domain Path: /languages
|
10 |
* WC requires at least: 4.0
|
11 |
+
* WC tested up to: 4.4
|
12 |
* Requires WP: 5.3
|
13 |
+
* Version: 1.4.0
|
14 |
*
|
15 |
* @package WooCommerce\Payments
|
16 |
*/
|
29 |
* Initialize the Jetpack connection functionality.
|
30 |
*/
|
31 |
function wcpay_jetpack_init() {
|
32 |
+
if ( ! wcpay_check_old_jetpack_version() ) {
|
33 |
+
return;
|
34 |
+
}
|
35 |
$jetpack_config = new Automattic\Jetpack\Config();
|
36 |
$jetpack_config->ensure(
|
37 |
'connection',
|
55 |
}
|
56 |
|
57 |
// Make sure this is run *after* WooCommerce has a chance to initialize its packages (wc-admin, etc). That is run with priority 10.
|
58 |
+
// If you change the priority of this action, you'll need to change it in the wcpay_check_old_jetpack_version function too.
|
59 |
add_action( 'plugins_loaded', 'wcpay_init', 11 );
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Check if WCPay is installed alongside an old version of Jetpack (8.1 or earlier). Due to the autoloader code in those old
|
63 |
+
* versions, the Jetpack Config initialization code would just crash the site.
|
64 |
+
* TODO: Remove this when Jetpack 8.1 (Released on January 2020) is so old we don't think anyone will run into this problem anymore.
|
65 |
+
*
|
66 |
+
* @return bool True if the plugin can keep initializing itself, false otherwise.
|
67 |
+
*/
|
68 |
+
function wcpay_check_old_jetpack_version() {
|
69 |
+
if ( defined( 'JETPACK__VERSION' ) && version_compare( JETPACK__VERSION, '8.2', '<' ) ) {
|
70 |
+
add_filter( 'admin_notices', 'wcpay_show_old_jetpack_notice' );
|
71 |
+
// Prevent the rest of the plugin from initializing.
|
72 |
+
remove_action( 'plugins_loaded', 'wcpay_init', 11 );
|
73 |
+
return false;
|
74 |
+
}
|
75 |
+
return true;
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Display an error notice if the installed Jetpack version is too old to even start initializing the plugin.
|
80 |
+
*/
|
81 |
+
function wcpay_show_old_jetpack_notice() {
|
82 |
+
?>
|
83 |
+
<div class="notice wcpay-notice notice-error">
|
84 |
+
<p><b><?php echo esc_html( __( 'WooCommerce Payments', 'woocommerce-payments' ) ); ?></b></p>
|
85 |
+
<p><?php echo esc_html( __( 'The version of Jetpack installed is too old to be used with WooCommerce Payments. WooCommerce Payments has been disabled. Please deactivate or update Jetpack.', 'woocommerce-payments' ) ); ?></p>
|
86 |
+
</div>
|
87 |
+
<?php
|
88 |
+
}
|