mirror of
https://github.com/pronamic/woocommerce-subscriptions.git
synced 2025-10-17 06:42:55 +00:00
Updates to 6.9.0
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
"plugins": [
|
"plugins": [
|
||||||
"https://downloads.wordpress.org/plugin/woocommerce.zip",
|
"https://downloads.wordpress.org/plugin/woocommerce.zip",
|
||||||
"https://downloads.wordpress.org/plugin/email-log.zip",
|
"https://downloads.wordpress.org/plugin/email-log.zip",
|
||||||
"https://github.com/woocommerce/woocommerce-gateway-dummy/releases/download/1.0.8/woocommerce-gateway-dummy.zip",
|
"https://github.com/woocommerce/woocommerce-gateway-dummy/releases/download/1.0.9/woocommerce-gateway-dummy.zip",
|
||||||
".",
|
".",
|
||||||
"./tests/e2e/test-configuration-plugin"
|
"./tests/e2e/test-configuration-plugin"
|
||||||
],
|
],
|
||||||
|
@@ -173,6 +173,10 @@ install_db() {
|
|||||||
mysqladmin create $DB_NAME $MYSQLADMIN_FLAGS
|
mysqladmin create $DB_NAME $MYSQLADMIN_FLAGS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_legacy_rest_api() {
|
||||||
|
wp plugin install https://downloads.wordpress.org/plugin/woocommerce-legacy-rest-api.1.0.4.zip --activate
|
||||||
|
}
|
||||||
|
|
||||||
install_woocommerce() {
|
install_woocommerce() {
|
||||||
WC_INSTALL_EXTRA=''
|
WC_INSTALL_EXTRA=''
|
||||||
INSTALLED_WC_VERSION=$(wp plugin get woocommerce --field=version)
|
INSTALLED_WC_VERSION=$(wp plugin get woocommerce --field=version)
|
||||||
@@ -202,4 +206,5 @@ install_wp
|
|||||||
install_db
|
install_db
|
||||||
configure_wp
|
configure_wp
|
||||||
install_test_suite
|
install_test_suite
|
||||||
|
install_legacy_rest_api
|
||||||
install_woocommerce
|
install_woocommerce
|
||||||
|
@@ -1,5 +1,16 @@
|
|||||||
*** WooCommerce Subscriptions Changelog ***
|
*** WooCommerce Subscriptions Changelog ***
|
||||||
|
|
||||||
|
2024-11-14 - version 6.9.0
|
||||||
|
* Add: New Customer Notification feature - sends reminder emails for upcoming subscription renewals, trials ending, and subscription expirations.
|
||||||
|
* Fix: Prevent adding products to the cart if a subscription renewal is already present.
|
||||||
|
* Update: Improved performance of wcs_get_subscription() when querying by product and customer or order.
|
||||||
|
* Update: Improved performance when checking limited subscription product availability.
|
||||||
|
* Update: Deprecate upgrading from versions of WooCommerce Subscriptions prior to 3.0.0 (released Jan 2020).
|
||||||
|
* Dev: Minor refactoring of `init` method in `WC_Subscriptions_Upgrader` class.
|
||||||
|
* Dev: Introduce the filter `woocommerce_subscriptions_synced_first_renewal_payment_timestamp` to enable plugins to modify the first renewal date of synced subscriptions.
|
||||||
|
* Dev: Update `get_post_meta()` calls to fetch product meta using CRUD getters.
|
||||||
|
* Dev: Update subscriptions-core to 7.7.1
|
||||||
|
|
||||||
2024-10-14 - version 6.8.0
|
2024-10-14 - version 6.8.0
|
||||||
* Fix: Restore Retry icon in Orders table for HPOS-enabled stores.
|
* Fix: Restore Retry icon in Orders table for HPOS-enabled stores.
|
||||||
* Fix: Correctly updates a subscription status to `cancelled` during a payment failure call when the current status is `pending-cancel`.
|
* Fix: Correctly updates a subscription status to `cancelled` during a payment failure call when the current status is `pending-cancel`.
|
||||||
|
@@ -150,6 +150,7 @@ class WCS_Retry_Post_Store extends WCS_Retry_Store {
|
|||||||
'limit' => -1,
|
'limit' => -1,
|
||||||
) );
|
) );
|
||||||
|
|
||||||
|
// We need to keep this call to `get_posts` since this is a custom post type (`payment_retry`), not a custom order type.
|
||||||
$retry_post_ids = get_posts( array(
|
$retry_post_ids = get_posts( array(
|
||||||
'posts_per_page' => $args['limit'],
|
'posts_per_page' => $args['limit'],
|
||||||
'post_type' => self::$post_type,
|
'post_type' => self::$post_type,
|
||||||
|
@@ -20,14 +20,13 @@ class WCS_Retry_Email {
|
|||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
public static function init() {
|
public static function init() {
|
||||||
|
add_action( 'woocommerce_email_classes', array( __CLASS__, 'add_emails' ), 12, 1 );
|
||||||
|
|
||||||
add_action( 'woocommerce_email_classes', __CLASS__ . '::add_emails', 12, 1 );
|
add_action( 'woocommerce_subscriptions_after_apply_retry_rule', array( __CLASS__, 'send_email' ), 0, 2 );
|
||||||
|
|
||||||
add_action( 'woocommerce_subscriptions_after_apply_retry_rule', __CLASS__ . '::send_email', 0, 2 );
|
add_action( 'woocommerce_order_status_failed', array( __CLASS__, 'maybe_detach_email' ), 9 );
|
||||||
|
|
||||||
add_action( 'woocommerce_order_status_failed', __CLASS__ . '::maybe_detach_email', 9 );
|
add_action( 'woocommerce_order_status_changed', array( __CLASS__, 'maybe_reattach_email' ), 100, 3 );
|
||||||
|
|
||||||
add_action( 'woocommerce_order_status_changed', __CLASS__ . '::maybe_reattach_email', 100, 3 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,8 +46,8 @@ class WCS_Retry_Email {
|
|||||||
*
|
*
|
||||||
* Attached to 'woocommerce_subscriptions_after_apply_retry_rule' with a low priority.
|
* Attached to 'woocommerce_subscriptions_after_apply_retry_rule' with a low priority.
|
||||||
*
|
*
|
||||||
* @param WCS_Retry_Rule The retry rule applied.
|
* @param WCS_Retry_Rule $retry_rule The retry rule applied.
|
||||||
* @param WC_Order The order to which the retry rule was applied.
|
* @param WC_Order $last_order The order to which the retry rule was applied.
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
public static function send_email( $retry_rule, $last_order ) {
|
public static function send_email( $retry_rule, $last_order ) {
|
||||||
@@ -60,7 +59,7 @@ class WCS_Retry_Email {
|
|||||||
$email_class = $retry_rule->get_email_template( $recipient );
|
$email_class = $retry_rule->get_email_template( $recipient );
|
||||||
if ( class_exists( $email_class ) ) {
|
if ( class_exists( $email_class ) ) {
|
||||||
$email = new $email_class();
|
$email = new $email_class();
|
||||||
$email->trigger( wcs_get_objects_property( $last_order, 'id' ), $last_order );
|
$email->trigger( $last_order->get_id(), $last_order );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1200,7 +1200,7 @@ class WC_Subscriptions_Switcher {
|
|||||||
WC_Subscriptions_Checkout::add_shipping( $subscription, $recurring_cart );
|
WC_Subscriptions_Checkout::add_shipping( $subscription, $recurring_cart );
|
||||||
|
|
||||||
// Now update subscription object order_shipping to reflect updated values so it doesn't stay 0
|
// Now update subscription object order_shipping to reflect updated values so it doesn't stay 0
|
||||||
$subscription->order_shipping = get_post_meta( $subscription->get_id(), '_order_shipping', true );
|
$subscription->order_shipping = $subscription->get_meta( '_order_shipping', true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2460,12 +2460,10 @@ class WC_Subscriptions_Switcher {
|
|||||||
* @deprecated 2.1
|
* @deprecated 2.1
|
||||||
*/
|
*/
|
||||||
public static function maybe_set_payment_method( $payment_processing_result, $order_id ) {
|
public static function maybe_set_payment_method( $payment_processing_result, $order_id ) {
|
||||||
|
|
||||||
_deprecated_function( __METHOD__, '2.1', __CLASS__ . '::maybe_set_payment_method_after_switch( $order )' );
|
_deprecated_function( __METHOD__, '2.1', __CLASS__ . '::maybe_set_payment_method_after_switch( $order )' );
|
||||||
|
|
||||||
if ( wcs_order_contains_switch( $order_id ) && false != get_post_meta( $order_id, '_paid_date', true ) ) {
|
$order = wc_get_order( $order_id );
|
||||||
|
if ( wcs_order_contains_switch( $order_id ) && false !== (bool) $order->get_meta( '_paid_date', true ) ) {
|
||||||
$order = wc_get_order( $order_id );
|
|
||||||
self::maybe_set_payment_method_after_switch( $order );
|
self::maybe_set_payment_method_after_switch( $order );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2808,13 +2806,10 @@ class WC_Subscriptions_Switcher {
|
|||||||
_deprecated_function( __METHOD__, '2.0' );
|
_deprecated_function( __METHOD__, '2.0' );
|
||||||
|
|
||||||
$subscription = wcs_get_subscription_from_key( $subscription_key );
|
$subscription = wcs_get_subscription_from_key( $subscription_key );
|
||||||
|
|
||||||
if ( $subscription->has_status( 'active' ) && $subscription->get_parent_id() && wcs_order_contains_switch( $subscription->get_parent_id() ) && 1 >= $subscription->get_payment_count() ) {
|
if ( $subscription->has_status( 'active' ) && $subscription->get_parent_id() && wcs_order_contains_switch( $subscription->get_parent_id() ) && 1 >= $subscription->get_payment_count() ) {
|
||||||
|
$first_payment_timestamp = (int) $subscription->get_meta( '_switched_subscription_first_payment_timestamp', true );
|
||||||
$first_payment_timestamp = get_post_meta( $subscription->get_parent_id(), '_switched_subscription_first_payment_timestamp', true );
|
if ( 0 !== $first_payment_timestamp ) {
|
||||||
|
$next_payment_date = ( 'mysql' === $type ) ? gmdate( 'Y-m-d H:i:s', $first_payment_timestamp ) : $first_payment_timestamp;
|
||||||
if ( 0 != $first_payment_timestamp ) {
|
|
||||||
$next_payment_date = ( 'mysql' == $type ) ? gmdate( 'Y-m-d H:i:s', $first_payment_timestamp ) : $first_payment_timestamp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
2
vendor/autoload.php
vendored
2
vendor/autoload.php
vendored
@@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
|
|||||||
|
|
||||||
require_once __DIR__ . '/composer/autoload_real.php';
|
require_once __DIR__ . '/composer/autoload_real.php';
|
||||||
|
|
||||||
return ComposerAutoloaderInit13fede8eb22b3733f757156a27f6ebe3::getLoader();
|
return ComposerAutoloaderInitf715afa45653daac47d4c3d21522dae0::getLoader();
|
||||||
|
8
vendor/composer/autoload_real.php
vendored
8
vendor/composer/autoload_real.php
vendored
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// autoload_real.php @generated by Composer
|
// autoload_real.php @generated by Composer
|
||||||
|
|
||||||
class ComposerAutoloaderInit13fede8eb22b3733f757156a27f6ebe3
|
class ComposerAutoloaderInitf715afa45653daac47d4c3d21522dae0
|
||||||
{
|
{
|
||||||
private static $loader;
|
private static $loader;
|
||||||
|
|
||||||
@@ -24,12 +24,12 @@ class ComposerAutoloaderInit13fede8eb22b3733f757156a27f6ebe3
|
|||||||
|
|
||||||
require __DIR__ . '/platform_check.php';
|
require __DIR__ . '/platform_check.php';
|
||||||
|
|
||||||
spl_autoload_register(array('ComposerAutoloaderInit13fede8eb22b3733f757156a27f6ebe3', 'loadClassLoader'), true, true);
|
spl_autoload_register(array('ComposerAutoloaderInitf715afa45653daac47d4c3d21522dae0', 'loadClassLoader'), true, true);
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||||
spl_autoload_unregister(array('ComposerAutoloaderInit13fede8eb22b3733f757156a27f6ebe3', 'loadClassLoader'));
|
spl_autoload_unregister(array('ComposerAutoloaderInitf715afa45653daac47d4c3d21522dae0', 'loadClassLoader'));
|
||||||
|
|
||||||
require __DIR__ . '/autoload_static.php';
|
require __DIR__ . '/autoload_static.php';
|
||||||
call_user_func(\Composer\Autoload\ComposerStaticInit13fede8eb22b3733f757156a27f6ebe3::getInitializer($loader));
|
call_user_func(\Composer\Autoload\ComposerStaticInitf715afa45653daac47d4c3d21522dae0::getInitializer($loader));
|
||||||
|
|
||||||
$loader->register(true);
|
$loader->register(true);
|
||||||
|
|
||||||
|
8
vendor/composer/autoload_static.php
vendored
8
vendor/composer/autoload_static.php
vendored
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
namespace Composer\Autoload;
|
namespace Composer\Autoload;
|
||||||
|
|
||||||
class ComposerStaticInit13fede8eb22b3733f757156a27f6ebe3
|
class ComposerStaticInitf715afa45653daac47d4c3d21522dae0
|
||||||
{
|
{
|
||||||
public static $prefixLengthsPsr4 = array (
|
public static $prefixLengthsPsr4 = array (
|
||||||
'C' =>
|
'C' =>
|
||||||
@@ -129,9 +129,9 @@ class ComposerStaticInit13fede8eb22b3733f757156a27f6ebe3
|
|||||||
public static function getInitializer(ClassLoader $loader)
|
public static function getInitializer(ClassLoader $loader)
|
||||||
{
|
{
|
||||||
return \Closure::bind(function () use ($loader) {
|
return \Closure::bind(function () use ($loader) {
|
||||||
$loader->prefixLengthsPsr4 = ComposerStaticInit13fede8eb22b3733f757156a27f6ebe3::$prefixLengthsPsr4;
|
$loader->prefixLengthsPsr4 = ComposerStaticInitf715afa45653daac47d4c3d21522dae0::$prefixLengthsPsr4;
|
||||||
$loader->prefixDirsPsr4 = ComposerStaticInit13fede8eb22b3733f757156a27f6ebe3::$prefixDirsPsr4;
|
$loader->prefixDirsPsr4 = ComposerStaticInitf715afa45653daac47d4c3d21522dae0::$prefixDirsPsr4;
|
||||||
$loader->classMap = ComposerStaticInit13fede8eb22b3733f757156a27f6ebe3::$classMap;
|
$loader->classMap = ComposerStaticInitf715afa45653daac47d4c3d21522dae0::$classMap;
|
||||||
|
|
||||||
}, null, ClassLoader::class);
|
}, null, ClassLoader::class);
|
||||||
}
|
}
|
||||||
|
14
vendor/composer/installed.json
vendored
14
vendor/composer/installed.json
vendored
@@ -156,17 +156,17 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "woocommerce/subscriptions-core",
|
"name": "woocommerce/subscriptions-core",
|
||||||
"version": "7.6.0",
|
"version": "7.7.1",
|
||||||
"version_normalized": "7.6.0.0",
|
"version_normalized": "7.7.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Automattic/woocommerce-subscriptions-core.git",
|
"url": "https://github.com/Automattic/woocommerce-subscriptions-core.git",
|
||||||
"reference": "6bfbf519df799dde8cd9f39e6c93e820e5170352"
|
"reference": "07bf070a5b2c9716bb20280055c4f3f07d83faed"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Automattic/woocommerce-subscriptions-core/zipball/6bfbf519df799dde8cd9f39e6c93e820e5170352",
|
"url": "https://api.github.com/repos/Automattic/woocommerce-subscriptions-core/zipball/07bf070a5b2c9716bb20280055c4f3f07d83faed",
|
||||||
"reference": "6bfbf519df799dde8cd9f39e6c93e820e5170352",
|
"reference": "07bf070a5b2c9716bb20280055c4f3f07d83faed",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
"woocommerce/woocommerce-sniffs": "0.1.0",
|
"woocommerce/woocommerce-sniffs": "0.1.0",
|
||||||
"yoast/phpunit-polyfills": "1.1.0"
|
"yoast/phpunit-polyfills": "1.1.0"
|
||||||
},
|
},
|
||||||
"time": "2024-10-14T04:42:25+00:00",
|
"time": "2024-11-13T23:26:20+00:00",
|
||||||
"type": "wordpress-plugin",
|
"type": "wordpress-plugin",
|
||||||
"extra": {
|
"extra": {
|
||||||
"phpcodesniffer-search-depth": 2
|
"phpcodesniffer-search-depth": 2
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
"description": "Sell products and services with recurring payments in your WooCommerce Store.",
|
"description": "Sell products and services with recurring payments in your WooCommerce Store.",
|
||||||
"homepage": "https://github.com/Automattic/woocommerce-subscriptions-core",
|
"homepage": "https://github.com/Automattic/woocommerce-subscriptions-core",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/Automattic/woocommerce-subscriptions-core/tree/7.6.0",
|
"source": "https://github.com/Automattic/woocommerce-subscriptions-core/tree/7.7.1",
|
||||||
"issues": "https://github.com/Automattic/woocommerce-subscriptions-core/issues"
|
"issues": "https://github.com/Automattic/woocommerce-subscriptions-core/issues"
|
||||||
},
|
},
|
||||||
"install-path": "../woocommerce/subscriptions-core"
|
"install-path": "../woocommerce/subscriptions-core"
|
||||||
|
18
vendor/composer/installed.php
vendored
18
vendor/composer/installed.php
vendored
@@ -1,9 +1,9 @@
|
|||||||
<?php return array(
|
<?php return array(
|
||||||
'root' => array(
|
'root' => array(
|
||||||
'name' => 'woocommerce/woocommerce-subscriptions',
|
'name' => 'woocommerce/woocommerce-subscriptions',
|
||||||
'pretty_version' => 'dev-release/6.8.0',
|
'pretty_version' => 'dev-release/6.9.0',
|
||||||
'version' => 'dev-release/6.8.0',
|
'version' => 'dev-release/6.9.0',
|
||||||
'reference' => '08730421b262b38602c261c857ac113aa46d7cbf',
|
'reference' => 'a8ffc66efa71aa0598db1c342818a4cdccfc85cb',
|
||||||
'type' => 'wordpress-plugin',
|
'type' => 'wordpress-plugin',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
@@ -32,18 +32,18 @@
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
'woocommerce/subscriptions-core' => array(
|
'woocommerce/subscriptions-core' => array(
|
||||||
'pretty_version' => '7.6.0',
|
'pretty_version' => '7.7.1',
|
||||||
'version' => '7.6.0.0',
|
'version' => '7.7.1.0',
|
||||||
'reference' => '6bfbf519df799dde8cd9f39e6c93e820e5170352',
|
'reference' => '07bf070a5b2c9716bb20280055c4f3f07d83faed',
|
||||||
'type' => 'wordpress-plugin',
|
'type' => 'wordpress-plugin',
|
||||||
'install_path' => __DIR__ . '/../woocommerce/subscriptions-core',
|
'install_path' => __DIR__ . '/../woocommerce/subscriptions-core',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'woocommerce/woocommerce-subscriptions' => array(
|
'woocommerce/woocommerce-subscriptions' => array(
|
||||||
'pretty_version' => 'dev-release/6.8.0',
|
'pretty_version' => 'dev-release/6.9.0',
|
||||||
'version' => 'dev-release/6.8.0',
|
'version' => 'dev-release/6.9.0',
|
||||||
'reference' => '08730421b262b38602c261c857ac113aa46d7cbf',
|
'reference' => 'a8ffc66efa71aa0598db1c342818a4cdccfc85cb',
|
||||||
'type' => 'wordpress-plugin',
|
'type' => 'wordpress-plugin',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
|
@@ -877,6 +877,10 @@ span.product-type.variable-subscription::before {
|
|||||||
width: 400px;
|
width: 400px;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
table.form-table input#woocommerce_subscriptions_customer_notifications_offset {
|
||||||
|
/* Aligns with .woocommerce table.form-table select */
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Reports Page */
|
/* Reports Page */
|
||||||
.woocommerce-reports-wide .postbox .chart-legend li a {
|
.woocommerce-reports-wide .postbox .chart-legend li a {
|
||||||
|
@@ -932,6 +932,9 @@ jQuery( function ( $ ) {
|
|||||||
),
|
),
|
||||||
$syncRenewals = $(
|
$syncRenewals = $(
|
||||||
document.getElementById( 'woocommerce_subscriptions_sync_payments' )
|
document.getElementById( 'woocommerce_subscriptions_sync_payments' )
|
||||||
|
),
|
||||||
|
$customerNotifications = $(
|
||||||
|
document.getElementById( 'woocommerce_subscriptions_customer_notifications_enabled' )
|
||||||
);
|
);
|
||||||
|
|
||||||
// We're on the Subscriptions settings page
|
// We're on the Subscriptions settings page
|
||||||
@@ -954,6 +957,9 @@ jQuery( function ( $ ) {
|
|||||||
).parents( 'tr' ),
|
).parents( 'tr' ),
|
||||||
$suspensionExtensionRow = $(
|
$suspensionExtensionRow = $(
|
||||||
'#woocommerce_subscriptions_recoup_suspension'
|
'#woocommerce_subscriptions_recoup_suspension'
|
||||||
|
).parents( 'tr' ),
|
||||||
|
$customerNotificationOffsetRow = $(
|
||||||
|
'#woocommerce_subscriptions_customer_notifications_offset'
|
||||||
).parents( 'tr' );
|
).parents( 'tr' );
|
||||||
|
|
||||||
// No animation for initial hiding when switching is disabled.
|
// No animation for initial hiding when switching is disabled.
|
||||||
@@ -1009,6 +1015,19 @@ jQuery( function ( $ ) {
|
|||||||
$daysNoFeeRow.fadeOut();
|
$daysNoFeeRow.fadeOut();
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
// No animation when initially hiding customer notification offset row.
|
||||||
|
if ( ! $customerNotifications.is( ':checked' ) ) {
|
||||||
|
$customerNotificationOffsetRow.hide();
|
||||||
|
}
|
||||||
|
// Watch the enable/disable customer notifications checkbox for changes.
|
||||||
|
$customerNotifications.on( 'change', function () {
|
||||||
|
if ( $( this ).is( ':checked' ) ) {
|
||||||
|
$customerNotificationOffsetRow.fadeIn();
|
||||||
|
} else {
|
||||||
|
$customerNotificationOffsetRow.fadeOut();
|
||||||
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't display the variation notice for variable subscription products
|
// Don't display the variation notice for variable subscription products
|
||||||
|
@@ -1,3 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* @deprecated subscriptions-core 7.7.0 This file is no longer in use and can be removed in future.
|
||||||
|
*/
|
||||||
jQuery( function ( $ ) {
|
jQuery( function ( $ ) {
|
||||||
var upgrade_start_time = null,
|
var upgrade_start_time = null,
|
||||||
total_subscriptions = wcs_update_script_data.subscription_count;
|
total_subscriptions = wcs_update_script_data.subscription_count;
|
||||||
|
@@ -1,5 +1,19 @@
|
|||||||
*** WooCommerce Subscriptions Core Changelog ***
|
*** WooCommerce Subscriptions Core Changelog ***
|
||||||
|
|
||||||
|
= 7.7.1 - 2024-11-13 =
|
||||||
|
* Fix - Only show the individual subscription information in customer notification emails, not all subscriptions purchased in the initial order.
|
||||||
|
* Fix - Resolved issues with Customer Notification emails not being sent due to unsupported emoji used in the default email subject.
|
||||||
|
|
||||||
|
= 7.7.0 - 2024-11-13 =
|
||||||
|
* Add - New Customer Notification feature: sends reminder emails for upcoming subscription renewals, trials ending, and subscription expirations.
|
||||||
|
* Fix - Prevent adding products to the cart if a subscription renewal is already present.
|
||||||
|
* Update - Improved performance of wcs_get_subscription() when querying by product and customer or order.
|
||||||
|
* Update - Improved performance when checking limited subscription product availability.
|
||||||
|
* Update - Deprecate upgrading from versions of WooCommerce Subscriptions prior to 3.0.0 (released Jan 2020).
|
||||||
|
* Dev - Minor refactoring of `init` method in `WC_Subscriptions_Upgrader` class.
|
||||||
|
* Dev - Introduce the filter `woocommerce_subscriptions_synced_first_renewal_payment_timestamp` to enable plugins to modify the first renewal date of synced subscriptions.
|
||||||
|
* Dev - Update `get_post_meta()` calls to fetch product meta using CRUD getters.
|
||||||
|
|
||||||
= 7.6.0 - 2024-10-14 =
|
= 7.6.0 - 2024-10-14 =
|
||||||
* Fix - Correctly updates a subscription status to `cancelled` during a payment failure call when the current status is `pending-cancel`.
|
* Fix - Correctly updates a subscription status to `cancelled` during a payment failure call when the current status is `pending-cancel`.
|
||||||
* Fix - Clear the `cancelled_email_sent` meta when a subscription is reactivated to allow the customer to receive future cancellation emails.
|
* Fix - Clear the `cancelled_email_sent` meta when a subscription is reactivated to allow the customer to receive future cancellation emails.
|
||||||
|
@@ -300,7 +300,8 @@ class WC_Subscriptions_Admin {
|
|||||||
$trial_tooltip = sprintf( _x( 'An optional period of time to wait before charging the first recurring payment. Any sign up fee will still be charged at the outset of the subscription. %s', 'Trial period field tooltip on Edit Product administration screen', 'woocommerce-subscriptions' ), self::get_trial_period_validation_message() );
|
$trial_tooltip = sprintf( _x( 'An optional period of time to wait before charging the first recurring payment. Any sign up fee will still be charged at the outset of the subscription. %s', 'Trial period field tooltip on Edit Product administration screen', 'woocommerce-subscriptions' ), self::get_trial_period_validation_message() );
|
||||||
|
|
||||||
// Set month as the default billing period
|
// Set month as the default billing period
|
||||||
if ( ! $chosen_period = get_post_meta( $post->ID, '_subscription_period', true ) ) {
|
$chosen_period = get_post_meta( $post->ID, '_subscription_period', true );
|
||||||
|
if ( ! $chosen_period ) {
|
||||||
$chosen_period = 'month';
|
$chosen_period = 'month';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -191,7 +191,7 @@ class WCS_Admin_Notice {
|
|||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.0
|
||||||
*/
|
*/
|
||||||
public function print_dismiss_url() {
|
public function print_dismiss_url() {
|
||||||
echo esc_attr( $this->dismiss_url );
|
echo esc_url( $this->dismiss_url );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Getters */
|
/* Getters */
|
||||||
|
@@ -0,0 +1,350 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce Subscriptions Notifications Debug Tool Processor.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Subscriptions
|
||||||
|
* @category Class
|
||||||
|
* @since 7.7.0
|
||||||
|
*/
|
||||||
|
class WCS_Notifications_Debug_Tool_Processor implements WCS_Batch_Processor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Option name for the tool state.
|
||||||
|
* This is used to pass the state of the tool between requests.
|
||||||
|
*/
|
||||||
|
const TOOL_STATE_OPTION_NAME = 'wcs_notifications_debug_tool_state';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
add_filter( 'woocommerce_debug_tools', array( $this, 'handle_woocommerce_debug_tools' ), 999, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the state of the tool.
|
||||||
|
*
|
||||||
|
* @return array {
|
||||||
|
* @last_offset Last offset processed.
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
private function get_tool_state(): array {
|
||||||
|
return (array) get_option( self::TOOL_STATE_OPTION_NAME, array() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the state of the tool.
|
||||||
|
*
|
||||||
|
* @param array $state New state of the tool.
|
||||||
|
*/
|
||||||
|
private function update_tool_state( $state ) {
|
||||||
|
update_option( self::TOOL_STATE_OPTION_NAME, $state );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the state of the tool.
|
||||||
|
*/
|
||||||
|
private function delete_tool_state() {
|
||||||
|
delete_option( self::TOOL_STATE_OPTION_NAME );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user-friendly name for this processor.
|
||||||
|
*
|
||||||
|
* @return string Name of the processor.
|
||||||
|
*/
|
||||||
|
public function get_name(): string {
|
||||||
|
return 'wcs_notifications_debug_tool_processor';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user-friendly description for this processor.
|
||||||
|
*
|
||||||
|
* @return string Description of what this processor does.
|
||||||
|
*/
|
||||||
|
public function get_description(): string {
|
||||||
|
return 'WooCommerce Notifications Debug Tool Processor';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the allowed subscription statuses to process.
|
||||||
|
*/
|
||||||
|
protected function get_subscription_statuses(): array {
|
||||||
|
$allowed_statuses = array(
|
||||||
|
'active',
|
||||||
|
'pending',
|
||||||
|
'on-hold',
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_map( 'wcs_sanitize_subscription_status_key', $allowed_statuses );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total number of pending items that require processing.
|
||||||
|
* Once an item is successfully processed by 'process_batch' it shouldn't be included in this count.
|
||||||
|
*
|
||||||
|
* Note that the once the processor is enqueued the batch processor controller will keep
|
||||||
|
* invoking `get_next_batch_to_process` and `process_batch` repeatedly until this method returns zero.
|
||||||
|
*
|
||||||
|
* In this case, this means total number of subscriptions in allowed statuses - number of processed subscriptions.
|
||||||
|
*
|
||||||
|
* @return int Number of items pending processing.
|
||||||
|
*/
|
||||||
|
public function get_total_pending_count(): int {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$allowed_statuses = $this->get_subscription_statuses();
|
||||||
|
$placeholders = implode( ', ', array_fill( 0, count( $allowed_statuses ), '%s' ) );
|
||||||
|
|
||||||
|
if ( wcs_is_custom_order_tables_usage_enabled() ) {
|
||||||
|
$total_subscriptions = $wpdb->get_var(
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT
|
||||||
|
COUNT(id)
|
||||||
|
FROM {$wpdb->prefix}wc_orders
|
||||||
|
WHERE type='shop_subscription'
|
||||||
|
AND status IN ($placeholders)
|
||||||
|
",
|
||||||
|
...$allowed_statuses
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$total_subscriptions = $wpdb->get_var(
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT
|
||||||
|
COUNT(ID)
|
||||||
|
FROM {$wpdb->prefix}posts
|
||||||
|
WHERE post_type='shop_subscription'
|
||||||
|
AND post_status IN ($placeholders)
|
||||||
|
",
|
||||||
|
...$allowed_statuses
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$state = $this->get_tool_state();
|
||||||
|
if ( isset( $state['last_offset'] ) ) {
|
||||||
|
$total_subscriptions -= (int) $state['last_offset'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $total_subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next batch of items that need to be processed.
|
||||||
|
*
|
||||||
|
* A batch item can be anything needed to identify the actual processing to be done,
|
||||||
|
* but whenever possible items should be numbers (e.g. database record ids)
|
||||||
|
* or at least strings, to ease troubleshooting and logging in case of problems.
|
||||||
|
*
|
||||||
|
* The size of the batch returned can be less than $size if there aren't that
|
||||||
|
* many items pending processing (and it can be zero if there isn't anything to process),
|
||||||
|
* but the size should always be consistent with what 'get_total_pending_count' returns
|
||||||
|
* (i.e. the size of the returned batch shouldn't be larger than the pending items count).
|
||||||
|
*
|
||||||
|
* @param int $size Maximum size of the batch to be returned.
|
||||||
|
*
|
||||||
|
* @return array Batch of items to process, containing $size or less items.
|
||||||
|
*/
|
||||||
|
public function get_next_batch_to_process( int $size ): array {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
$allowed_statuses = $this->get_subscription_statuses();
|
||||||
|
$placeholders = implode( ', ', array_fill( 0, count( $allowed_statuses ), '%s' ) );
|
||||||
|
$state = $this->get_tool_state();
|
||||||
|
$offset = isset( $state['last_offset'] ) ? (int) $state['last_offset'] : 0;
|
||||||
|
|
||||||
|
$args = array_merge(
|
||||||
|
$allowed_statuses,
|
||||||
|
array( $size ),
|
||||||
|
array( $offset ),
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( wcs_is_custom_order_tables_usage_enabled() ) {
|
||||||
|
$subscriptions_to_process = $wpdb->get_col(
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT
|
||||||
|
id
|
||||||
|
FROM {$wpdb->prefix}wc_orders
|
||||||
|
WHERE type='shop_subscription'
|
||||||
|
AND status IN ($placeholders)
|
||||||
|
ORDER BY id ASC
|
||||||
|
LIMIT %d
|
||||||
|
OFFSET %d",
|
||||||
|
...$args
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$subscriptions_to_process = $wpdb->get_col(
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT
|
||||||
|
ID
|
||||||
|
FROM {$wpdb->prefix}posts
|
||||||
|
WHERE post_type='shop_subscription'
|
||||||
|
AND post_status IN ($placeholders)
|
||||||
|
ORDER BY ID ASC
|
||||||
|
LIMIT %d
|
||||||
|
OFFSET %d",
|
||||||
|
...$args
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the tool state if there are no more subscriptions to process.
|
||||||
|
if ( empty( $subscriptions_to_process ) ) {
|
||||||
|
$this->delete_tool_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $subscriptions_to_process;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process data for the supplied batch.
|
||||||
|
*
|
||||||
|
* This method should be prepared to receive items that don't actually need processing
|
||||||
|
* (because they have been processed before) and ignore them, but if at least
|
||||||
|
* one of the batch items that actually need processing can't be processed, an exception should be thrown.
|
||||||
|
*
|
||||||
|
* Once an item has been processed it shouldn't be counted in 'get_total_pending_count'
|
||||||
|
* nor included in 'get_next_batch_to_process' anymore (unless something happens that causes it
|
||||||
|
* to actually require further processing).
|
||||||
|
*
|
||||||
|
* @throw \Exception Something went wrong while processing the batch.
|
||||||
|
*
|
||||||
|
* @param array $batch Batch to process, as returned by 'get_next_batch_to_process'.
|
||||||
|
*/
|
||||||
|
public function process_batch( array $batch ): void {
|
||||||
|
|
||||||
|
$subscriptions_notifications = WC_Subscriptions_Core_Plugin::instance()->notifications_scheduler;
|
||||||
|
|
||||||
|
foreach ( $batch as $subscription_id ) {
|
||||||
|
$subscription = wcs_get_subscription( $subscription_id );
|
||||||
|
|
||||||
|
if ( ! $subscription ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( WC_Subscriptions_Email_Notifications::notifications_globally_enabled() ) {
|
||||||
|
$subscriptions_notifications->update_status( $subscription, $subscription->get_status(), null );
|
||||||
|
} else {
|
||||||
|
$subscriptions_notifications->unschedule_all_notifications( $subscription );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the subscription's update time to mark it as updated.
|
||||||
|
$subscription->set_date_modified( time() );
|
||||||
|
$subscription->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tool state.
|
||||||
|
$state = $this->get_tool_state();
|
||||||
|
$state['last_offset'] = isset( $state['last_offset'] ) ? absint( $state['last_offset'] ) + count( $batch ) : count( $batch );
|
||||||
|
$this->update_tool_state( $state );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default (preferred) batch size to pass to 'get_next_batch_to_process'.
|
||||||
|
* The controller will pass this size unless it's externally configured
|
||||||
|
* to use a different size.
|
||||||
|
*
|
||||||
|
* @return int Default batch size.
|
||||||
|
*/
|
||||||
|
public function get_default_batch_size(): int {
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the background process for batch processing subscription notifications updates.
|
||||||
|
*
|
||||||
|
* @return string Informative string to show after the tool is triggered in UI.
|
||||||
|
*/
|
||||||
|
public function enqueue(): string {
|
||||||
|
$batch_processor = WCS_Batch_Processing_Controller::instance();
|
||||||
|
if ( $batch_processor->is_enqueued( self::class ) ) {
|
||||||
|
return __( 'Background process for updating subscription notifications already started, nothing done.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$batch_processor->enqueue_processor( self::class );
|
||||||
|
|
||||||
|
return __( 'Background process for updating subscription notifications started', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the background process for batch processing subscription notifications updates.
|
||||||
|
*
|
||||||
|
* @return string Informative string to show after the tool is triggered in UI.
|
||||||
|
*/
|
||||||
|
public function dequeue(): string {
|
||||||
|
$batch_processor = WCS_Batch_Processing_Controller::instance();
|
||||||
|
if ( ! $batch_processor->is_enqueued( self::class ) ) {
|
||||||
|
return __( 'Background process for updating subscription notifications not started, nothing done.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$batch_processor->remove_processor( self::class );
|
||||||
|
return __( 'Background process for updating subscription notifications stopped', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the tool to start or stop the background process that manages notification batch processing.
|
||||||
|
*
|
||||||
|
* @param array $tools Old tools array.
|
||||||
|
* @return array Updated tools array.
|
||||||
|
*/
|
||||||
|
public function handle_woocommerce_debug_tools( array $tools ): array {
|
||||||
|
|
||||||
|
if ( ! WC_Subscriptions_Email_Notifications::notifications_globally_enabled() ) {
|
||||||
|
|
||||||
|
$tools['start_add_subscription_notifications'] = array(
|
||||||
|
'name' => __( 'Regenerate subscription notifications', 'woocommerce-subscriptions' ),
|
||||||
|
'button' => __( 'Regenerate notifications', 'woocommerce-subscriptions' ),
|
||||||
|
'disabled' => true,
|
||||||
|
'desc' => sprintf(
|
||||||
|
'%1$s<br/><strong class="red">%2$s</strong> %3$s <a href="%4$s">%5$s</a>',
|
||||||
|
__( 'This tool will add notifications to pending, active, and on-hold subscriptions. These updates will occur gradually in the background using Action Scheduler.', 'woocommerce-subscriptions' ),
|
||||||
|
__( 'Note:', 'woocommerce-subscriptions' ),
|
||||||
|
__( 'Notifications are currently turned off. To activate them, check the "Enable customer renewal reminder notification emails." option (via WooCommerce > Settings > Subscriptions > Customer Notifications).', 'woocommerce-subscriptions' ),
|
||||||
|
esc_url( admin_url( 'admin.php?page=wc-settings&tab=subscriptions' ) ),
|
||||||
|
__( 'Manage settings.', 'woocommerce-subscriptions' ),
|
||||||
|
),
|
||||||
|
'requires_refresh' => true,
|
||||||
|
);
|
||||||
|
return $tools;
|
||||||
|
}
|
||||||
|
|
||||||
|
$batch_processor = WCS_Batch_Processing_Controller::instance();
|
||||||
|
|
||||||
|
if ( $batch_processor->is_enqueued( self::class ) ) {
|
||||||
|
|
||||||
|
$pending_count = $this->get_total_pending_count();
|
||||||
|
$tools['stop_add_subscription_notifications'] = array(
|
||||||
|
'name' => __( 'Regenerate subscription notifications', 'woocommerce-subscriptions' ),
|
||||||
|
'button' => __( 'Stop regenerating notifications', 'woocommerce-subscriptions' ),
|
||||||
|
'desc' =>
|
||||||
|
/* translators: %1$d=count of total entries needing conversion */
|
||||||
|
sprintf( __( 'Stopping this will halt the background process that adds notifications to pending, active, and on-hold subscriptions. %1$d subscriptions remain to be processed.', 'woocommerce-subscriptions' ), $pending_count ),
|
||||||
|
'callback' => array( $this, 'dequeue' ),
|
||||||
|
'requires_refresh' => true,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$tools['start_add_subscription_notifications'] = array(
|
||||||
|
'name' => __( 'Regenerate subscription notifications', 'woocommerce-subscriptions' ),
|
||||||
|
'button' => __( 'Regenerate notifications', 'woocommerce-subscriptions' ),
|
||||||
|
'desc' => __( 'This tool will regenerate notifications to pending, active, and on-hold subscriptions. These updates will occur gradually in the background using Action Scheduler.', 'woocommerce-subscriptions' ),
|
||||||
|
'callback' => array( $this, 'enqueue' ),
|
||||||
|
'requires_refresh' => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tools;
|
||||||
|
}
|
||||||
|
}
|
129
vendor/woocommerce/subscriptions-core/includes/class-wc-subscription-query-controller.php
vendored
Normal file
129
vendor/woocommerce/subscriptions-core/includes/class-wc-subscription-query-controller.php
vendored
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Subscription Query Controller
|
||||||
|
*
|
||||||
|
* This class is used to assist the wcs_get_subscriptions() query for something.
|
||||||
|
* It currently supports 1 feature to determine if the query should be filtered by product ID or variation ID after the query has been run.
|
||||||
|
*
|
||||||
|
* QUERYING SUBSCRIPTIONS BY PRODUCT
|
||||||
|
*
|
||||||
|
* Querying subscriptions by product or variation ID is an expensive database operation. This class provides methods to determine if a wcs_get_subscriptions()
|
||||||
|
* set of args would be better served by filtering the query results by product ID or variation ID after the query has been run, rather than querying for
|
||||||
|
* subscriptions by products.
|
||||||
|
*
|
||||||
|
* If the wcs_get_subscriptions() args are already limited by customer ID or order ID we know that the results will be sufficiently limited. In these cases, we can
|
||||||
|
* filter the results by product ID or variation ID after the query has been run.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Subscriptions
|
||||||
|
* @subpackage Component
|
||||||
|
* @since 6.9.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WC_Subscription_Query_Controller class.
|
||||||
|
*/
|
||||||
|
class WC_Subscription_Query_Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wcs_get_subscriptions() query variables.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $query_vars = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param array $query_vars The wcs_get_subscriptions() query variables.
|
||||||
|
*/
|
||||||
|
public function __construct( $query_vars ) {
|
||||||
|
$this->query_vars = $query_vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the query is for a specific product or variation.
|
||||||
|
*
|
||||||
|
* @return bool True if the query is for a specific product or variation, otherwise false.
|
||||||
|
*/
|
||||||
|
public function has_product_query() {
|
||||||
|
return ( 0 !== $this->query_vars['product_id'] && is_numeric( $this->query_vars['product_id'] ) ) || ( 0 !== $this->query_vars['variation_id'] && is_numeric( $this->query_vars['variation_id'] ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the wcs_get_subscription() query should filter the results by product ID or variation ID after the query has been run.
|
||||||
|
*
|
||||||
|
* If the wcs_get_subscriptions() query is substantially limited (eg to a customer or order) we know that the results will be small. In these cases, we can
|
||||||
|
* filter the results by product ID or variation ID after the query has been run for better performance.
|
||||||
|
*
|
||||||
|
* @return bool True if the subscriptions should be queried by product ID, otherwise false.
|
||||||
|
*/
|
||||||
|
public function should_filter_query_results() {
|
||||||
|
$can_filter_results = false;
|
||||||
|
$order_id = $this->query_vars['order_id'] ?? 0;
|
||||||
|
$customer_id = $this->query_vars['customer_id'] ?? 0;
|
||||||
|
|
||||||
|
// If we're querying by order ID or customer ID, we can filter the results by product ID after the query has been run.
|
||||||
|
if ( ! empty( $order_id ) || ! empty( $customer_id ) ) {
|
||||||
|
$can_filter_results = apply_filters( 'wcs_should_filter_subscriptions_results_by_product_id', true, $this->query_vars );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $can_filter_results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the subscription query results by product ID or variation ID.
|
||||||
|
*
|
||||||
|
* @param WC_Subscriptions[] $subscriptions
|
||||||
|
* @return WC_Subscriptions[] The filtered subscriptions.
|
||||||
|
*/
|
||||||
|
public function filter_subscriptions( $subscriptions ) {
|
||||||
|
$filtered_subscriptions = [];
|
||||||
|
$product_id = $this->query_vars['product_id'] ?? 0;
|
||||||
|
$variation_id = $this->query_vars['variation_id'] ?? 0;
|
||||||
|
|
||||||
|
if ( empty( $product_id ) && empty( $variation_id ) ) {
|
||||||
|
return $subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter the subscriptions by product ID or variation ID.
|
||||||
|
foreach ( $subscriptions as $subscription_id => $subscription ) {
|
||||||
|
if (
|
||||||
|
( $variation_id && $subscription->has_product( $variation_id ) ) ||
|
||||||
|
( $product_id && $subscription->has_product( $product_id ) )
|
||||||
|
) {
|
||||||
|
$filtered_subscriptions[ $subscription_id ] = $subscription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filtered_subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies pagination to the subscriptions array.
|
||||||
|
*
|
||||||
|
* @param WC_Subscriptions[] $subscriptions
|
||||||
|
* @return WC_Subscriptions[] The subscriptions array with pagination applied.
|
||||||
|
*/
|
||||||
|
public function paginate_results( $subscriptions ) {
|
||||||
|
$per_page = $this->query_vars['subscriptions_per_page'];
|
||||||
|
$page = $this->query_vars['paged'];
|
||||||
|
$offset = $this->query_vars['offset'];
|
||||||
|
|
||||||
|
// If the limit is -1, return all subscriptions.
|
||||||
|
if ( -1 === $per_page ) {
|
||||||
|
return $subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $offset ) {
|
||||||
|
$start_index = $offset;
|
||||||
|
} else {
|
||||||
|
// Calculate the starting index for the slice.
|
||||||
|
$start_index = ( $page - 1 ) * $per_page;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice the subscriptions array to get the required items.
|
||||||
|
return array_slice( $subscriptions, $start_index, $per_page, true );
|
||||||
|
}
|
||||||
|
}
|
@@ -18,7 +18,7 @@ class WC_Subscriptions_Cart_Validator {
|
|||||||
|
|
||||||
add_filter( 'woocommerce_add_to_cart_validation', array( __CLASS__, 'maybe_empty_cart' ), 10, 5 );
|
add_filter( 'woocommerce_add_to_cart_validation', array( __CLASS__, 'maybe_empty_cart' ), 10, 5 );
|
||||||
add_filter( 'woocommerce_cart_loaded_from_session', array( __CLASS__, 'validate_cart_contents_for_mixed_checkout' ), 10 );
|
add_filter( 'woocommerce_cart_loaded_from_session', array( __CLASS__, 'validate_cart_contents_for_mixed_checkout' ), 10 );
|
||||||
add_filter( 'woocommerce_add_to_cart_validation', array( __CLASS__, 'can_add_subscription_product_to_cart' ), 10, 6 );
|
add_filter( 'woocommerce_add_to_cart_validation', array( __CLASS__, 'can_add_product_to_cart' ), 10, 6 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,15 +119,23 @@ class WC_Subscriptions_Cart_Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't allow new subscription products to be added to the cart if it contains a subscription renewal already.
|
* Don't allow products to be added to the cart if it contains a subscription renewal already.
|
||||||
*
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.6.0
|
* @since 7.7.0
|
||||||
|
*
|
||||||
|
* @param bool $can_add Whether the product can be added to the cart.
|
||||||
|
* @param int $product_id The product ID.
|
||||||
|
* @param int $quantity The quantity of the product being added.
|
||||||
|
* @param int $variation_id The variation ID.
|
||||||
|
* @param array $variations The variations of the product being added.
|
||||||
|
* @param array $item_data The item data.
|
||||||
|
*
|
||||||
|
* @return bool Whether the product can be added to the cart.
|
||||||
*/
|
*/
|
||||||
public static function can_add_subscription_product_to_cart( $can_add, $product_id, $quantity, $variation_id = '', $variations = array(), $item_data = array() ) {
|
public static function can_add_product_to_cart( $can_add, $product_id, $quantity, $variation_id = '', $variations = array(), $item_data = array() ) {
|
||||||
|
if ( $can_add && ! isset( $item_data['subscription_renewal'] ) && wcs_cart_contains_renewal() ) {
|
||||||
|
wc_add_notice( __( 'That product can not be added to your cart as it already contains a subscription renewal.', 'woocommerce-subscriptions' ), 'error' );
|
||||||
|
|
||||||
if ( $can_add && ! isset( $item_data['subscription_renewal'] ) && wcs_cart_contains_renewal() && WC_Subscriptions_Product::is_subscription( $product_id ) ) {
|
|
||||||
|
|
||||||
wc_add_notice( __( 'That subscription product can not be added to your cart as it already contains a subscription renewal.', 'woocommerce-subscriptions' ), 'error' );
|
|
||||||
$can_add = false;
|
$can_add = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,4 +166,21 @@ class WC_Subscriptions_Cart_Validator {
|
|||||||
|
|
||||||
return $fragments;
|
return $fragments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't allow new subscription products to be added to the cart if it contains a subscription renewal already.
|
||||||
|
*
|
||||||
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.6.0
|
||||||
|
* @deprecated 3.0.0
|
||||||
|
*/
|
||||||
|
public static function can_add_subscription_product_to_cart( $can_add, $product_id, $quantity, $variation_id = '', $variations = array(), $item_data = array() ) {
|
||||||
|
wcs_deprecated_function( __METHOD__, '6.9.0', 'WC_Subscriptions_Cart_Validator::can_add_product_to_cart' );
|
||||||
|
if ( $can_add && ! isset( $item_data['subscription_renewal'] ) && wcs_cart_contains_renewal() && WC_Subscriptions_Product::is_subscription( $product_id ) ) {
|
||||||
|
wc_add_notice( __( 'That subscription product can not be added to your cart as it already contains a subscription renewal.', 'woocommerce-subscriptions' ), 'error' );
|
||||||
|
|
||||||
|
$can_add = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $can_add;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ class WC_Subscriptions_Core_Plugin {
|
|||||||
* The version of subscriptions-core library.
|
* The version of subscriptions-core library.
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $library_version = '7.6.0'; // WRCS: DEFINED_VERSION.
|
protected $library_version = '7.7.1'; // WRCS: DEFINED_VERSION.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The subscription scheduler instance.
|
* The subscription scheduler instance.
|
||||||
@@ -25,6 +25,13 @@ class WC_Subscriptions_Core_Plugin {
|
|||||||
*/
|
*/
|
||||||
protected $scheduler = null;
|
protected $scheduler = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification scheduler instance.
|
||||||
|
*
|
||||||
|
* @var WCS_Action_Scheduler_Customer_Notifications
|
||||||
|
*/
|
||||||
|
public $notifications_scheduler = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The plugin's autoloader instance.
|
* The plugin's autoloader instance.
|
||||||
*
|
*
|
||||||
@@ -125,6 +132,7 @@ class WC_Subscriptions_Core_Plugin {
|
|||||||
WC_Subscriptions_Renewal_Order::init();
|
WC_Subscriptions_Renewal_Order::init();
|
||||||
WC_Subscriptions_Checkout::init();
|
WC_Subscriptions_Checkout::init();
|
||||||
WC_Subscriptions_Email::init();
|
WC_Subscriptions_Email::init();
|
||||||
|
WC_Subscriptions_Email_Notifications::init();
|
||||||
WC_Subscriptions_Addresses::init();
|
WC_Subscriptions_Addresses::init();
|
||||||
WC_Subscriptions_Change_Payment_Gateway::init();
|
WC_Subscriptions_Change_Payment_Gateway::init();
|
||||||
$payment_gateways_handler::init();
|
$payment_gateways_handler::init();
|
||||||
@@ -156,10 +164,16 @@ class WC_Subscriptions_Core_Plugin {
|
|||||||
add_action( 'plugins_loaded', 'WCS_Related_Order_Store::instance' );
|
add_action( 'plugins_loaded', 'WCS_Related_Order_Store::instance' );
|
||||||
add_action( 'plugins_loaded', 'WCS_Customer_Store::instance' );
|
add_action( 'plugins_loaded', 'WCS_Customer_Store::instance' );
|
||||||
|
|
||||||
|
// Initialise the batch processing controller.
|
||||||
|
add_action( 'init', 'WCS_Batch_Processing_Controller::instance' );
|
||||||
|
|
||||||
// Initialise the scheduler.
|
// Initialise the scheduler.
|
||||||
$scheduler_class = apply_filters( 'woocommerce_subscriptions_scheduler', 'WCS_Action_Scheduler' );
|
$scheduler_class = apply_filters( 'woocommerce_subscriptions_scheduler', 'WCS_Action_Scheduler' );
|
||||||
$this->scheduler = new $scheduler_class();
|
$this->scheduler = new $scheduler_class();
|
||||||
|
|
||||||
|
// Customer notifications scheduler.
|
||||||
|
$this->notifications_scheduler = new WCS_Action_Scheduler_Customer_Notifications();
|
||||||
|
|
||||||
// Initialise the cache.
|
// Initialise the cache.
|
||||||
$this->cache = WCS_Cache_Manager::get_instance();
|
$this->cache = WCS_Cache_Manager::get_instance();
|
||||||
|
|
||||||
@@ -244,6 +258,8 @@ class WC_Subscriptions_Core_Plugin {
|
|||||||
add_action( 'init', array( $this, 'activate_plugin' ) );
|
add_action( 'init', array( $this, 'activate_plugin' ) );
|
||||||
|
|
||||||
add_filter( 'action_scheduler_queue_runner_batch_size', array( $this, 'reduce_multisite_action_scheduler_batch_size' ) );
|
add_filter( 'action_scheduler_queue_runner_batch_size', array( $this, 'reduce_multisite_action_scheduler_batch_size' ) );
|
||||||
|
|
||||||
|
add_action( 'init', array( $this, 'init_notification_batch_processor' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -517,6 +533,11 @@ class WC_Subscriptions_Core_Plugin {
|
|||||||
update_option( WC_Subscriptions_admin::$option_prefix . '_paypal_debugging_default_set', 'true' );
|
update_option( WC_Subscriptions_admin::$option_prefix . '_paypal_debugging_default_set', 'true' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable customer notifications by default for new stores.
|
||||||
|
if ( '0' === get_option( WC_Subscriptions_Admin::$option_prefix . '_previous_version', '0' ) && 'no' === get_option( WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$switch_setting_string, 'no' ) ) {
|
||||||
|
update_option( WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$switch_setting_string, 'yes' );
|
||||||
|
}
|
||||||
|
|
||||||
update_option( WC_Subscriptions_Admin::$option_prefix . '_is_active', true );
|
update_option( WC_Subscriptions_Admin::$option_prefix . '_is_active', true );
|
||||||
|
|
||||||
set_transient( $this->get_activation_transient(), true, 60 * 60 );
|
set_transient( $this->get_activation_transient(), true, 60 * 60 );
|
||||||
@@ -625,4 +646,15 @@ class WC_Subscriptions_Core_Plugin {
|
|||||||
|
|
||||||
return $batch_size;
|
return $batch_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize batch processing for subscription notifications.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function init_notification_batch_processor() {
|
||||||
|
// Background processing for notifications
|
||||||
|
$notifications_batch_processor = new WCS_Notifications_Batch_Processor();
|
||||||
|
$notifications_debug_tool_processor = new WCS_Notifications_Debug_Tool_Processor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
354
vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-email-notifications.php
vendored
Normal file
354
vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-email-notifications.php
vendored
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscriptions Email Notifications Class
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @package WooCommerce Subscriptions
|
||||||
|
* @subpackage WC_Subscriptions_Email
|
||||||
|
* @category Class
|
||||||
|
*/
|
||||||
|
class WC_Subscriptions_Email_Notifications {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string Offset setting option identifier.
|
||||||
|
*/
|
||||||
|
public static $offset_setting_string = '_customer_notifications_offset';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string Enabled/disabled setting option identifier.
|
||||||
|
*/
|
||||||
|
public static $switch_setting_string = '_customer_notifications_enabled';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init.
|
||||||
|
*/
|
||||||
|
public static function init() {
|
||||||
|
|
||||||
|
add_action( 'woocommerce_email_classes', [ __CLASS__, 'add_emails' ], 10, 1 );
|
||||||
|
|
||||||
|
add_action( 'woocommerce_init', [ __CLASS__, 'hook_notification_emails' ] );
|
||||||
|
|
||||||
|
// Add notification actions to the admin edit subscriptions page.
|
||||||
|
add_filter( 'woocommerce_order_actions', [ __CLASS__, 'add_notification_actions' ], 10, 1 );
|
||||||
|
|
||||||
|
// Trigger actions from Edit order screen.
|
||||||
|
add_action( 'woocommerce_order_action_wcs_customer_notification_free_trial_expiration', [ __CLASS__, 'forward_action' ], 10, 1 );
|
||||||
|
add_action( 'woocommerce_order_action_wcs_customer_notification_subscription_expiration', [ __CLASS__, 'forward_action' ], 10, 1 );
|
||||||
|
add_action( 'woocommerce_order_action_wcs_customer_notification_renewal', [ __CLASS__, 'forward_action' ], 10, 1 );
|
||||||
|
|
||||||
|
// Add settings UI.
|
||||||
|
add_filter( 'woocommerce_subscription_settings', [ __CLASS__, 'add_settings' ], 20 );
|
||||||
|
|
||||||
|
// Add admin notice.
|
||||||
|
add_action( 'admin_notices', [ __CLASS__, 'maybe_add_admin_notice' ] );
|
||||||
|
|
||||||
|
// Bump settings update time whenever related options change.
|
||||||
|
add_action( 'update_option_' . WC_Subscriptions_Admin::$option_prefix . self::$offset_setting_string, [ __CLASS__, 'set_notification_settings_update_time' ], 10, 3 );
|
||||||
|
add_action( 'update_option_' . WC_Subscriptions_Admin::$option_prefix . self::$switch_setting_string, [ __CLASS__, 'set_notification_settings_update_time' ], 10, 3 );
|
||||||
|
add_action( 'add_option_' . WC_Subscriptions_Admin::$option_prefix . self::$offset_setting_string, [ __CLASS__, 'set_notification_settings_update_time' ], 10, 2 );
|
||||||
|
add_action( 'add_option_' . WC_Subscriptions_Admin::$option_prefix . self::$switch_setting_string, [ __CLASS__, 'set_notification_settings_update_time' ], 10, 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map and forward Edit order screen action to the correct reminder.
|
||||||
|
*
|
||||||
|
* @param $order
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function forward_action( $order ) {
|
||||||
|
$trigger_action = '';
|
||||||
|
$current_action = current_action();
|
||||||
|
switch ( $current_action ) {
|
||||||
|
case 'woocommerce_order_action_wcs_customer_notification_free_trial_expiration':
|
||||||
|
$trigger_action = 'woocommerce_scheduled_subscription_customer_notification_trial_expiration';
|
||||||
|
break;
|
||||||
|
case 'woocommerce_order_action_wcs_customer_notification_subscription_expiration':
|
||||||
|
$trigger_action = 'woocommerce_scheduled_subscription_customer_notification_expiration';
|
||||||
|
break;
|
||||||
|
case 'woocommerce_order_action_wcs_customer_notification_renewal':
|
||||||
|
$trigger_action = 'woocommerce_scheduled_subscription_customer_notification_renewal';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $trigger_action ) {
|
||||||
|
do_action( $trigger_action, $order->get_id() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the update time when any of the settings that affect notifications change and triggers update of subscriptions.
|
||||||
|
*
|
||||||
|
* When time offset or global on/off switch change values, this method gets triggered and it:
|
||||||
|
* 1. Updates the wcs_notification_settings_update_time option so that the code knows which subscriptions to update
|
||||||
|
* 2. Triggers rescheduling/unscheduling of existing notifications.
|
||||||
|
* 3. Adds a notice with info about the actions that got triggered to the store manager.
|
||||||
|
*
|
||||||
|
* Side note: offset gets updated in WCS_Action_Scheduler_Customer_Notifications::set_time_offset_from_option.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function set_notification_settings_update_time() {
|
||||||
|
update_option( 'wcs_notification_settings_update_time', time() );
|
||||||
|
|
||||||
|
// Shortcut to unschedule all notifications more efficiently instead of processing them subscription by subscription.
|
||||||
|
if ( ! self::notifications_globally_enabled() ) {
|
||||||
|
as_unschedule_all_actions( null, [], 'wcs_customer_notifications' );
|
||||||
|
} else {
|
||||||
|
$message = WCS_Notifications_Batch_Processor::enqueue();
|
||||||
|
$admin_notice = new WCS_Admin_Notice( 'updated' );
|
||||||
|
$admin_notice->set_simple_content( $message );
|
||||||
|
$admin_notice->display();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Subscriptions notifications' email classes.
|
||||||
|
*/
|
||||||
|
public static function add_emails( $email_classes ) {
|
||||||
|
|
||||||
|
$email_classes['WCS_Email_Customer_Notification_Auto_Trial_Expiration'] = new WCS_Email_Customer_Notification_Auto_Trial_Expiration();
|
||||||
|
$email_classes['WCS_Email_Customer_Notification_Manual_Trial_Expiration'] = new WCS_Email_Customer_Notification_Manual_Trial_Expiration();
|
||||||
|
$email_classes['WCS_Email_Customer_Notification_Subscription_Expiration'] = new WCS_Email_Customer_Notification_Subscription_Expiration();
|
||||||
|
$email_classes['WCS_Email_Customer_Notification_Manual_Renewal'] = new WCS_Email_Customer_Notification_Manual_Renewal();
|
||||||
|
$email_classes['WCS_Email_Customer_Notification_Auto_Renewal'] = new WCS_Email_Customer_Notification_Auto_Renewal();
|
||||||
|
|
||||||
|
return $email_classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook the notification emails with our custom trigger.
|
||||||
|
*/
|
||||||
|
public static function hook_notification_emails() {
|
||||||
|
add_action( 'woocommerce_scheduled_subscription_customer_notification_renewal', [ __CLASS__, 'send_notification' ] );
|
||||||
|
add_action( 'woocommerce_scheduled_subscription_customer_notification_trial_expiration', [ __CLASS__, 'send_notification' ] );
|
||||||
|
add_action( 'woocommerce_scheduled_subscription_customer_notification_expiration', [ __CLASS__, 'send_notification' ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the notification emails.
|
||||||
|
*
|
||||||
|
* @param int $subscription_id Subscription ID.
|
||||||
|
*/
|
||||||
|
public static function send_notification( $subscription_id ) {
|
||||||
|
|
||||||
|
// Init email classes.
|
||||||
|
$emails = WC()->mailer()->get_emails();
|
||||||
|
|
||||||
|
if ( ! ( $emails['WCS_Email_Customer_Notification_Auto_Renewal'] instanceof WCS_Email_Customer_Notification_Auto_Renewal
|
||||||
|
&& $emails['WCS_Email_Customer_Notification_Manual_Renewal'] instanceof WCS_Email_Customer_Notification_Manual_Renewal
|
||||||
|
&& $emails['WCS_Email_Customer_Notification_Subscription_Expiration'] instanceof WCS_Email_Customer_Notification_Subscription_Expiration
|
||||||
|
&& $emails['WCS_Email_Customer_Notification_Manual_Trial_Expiration'] instanceof WCS_Email_Customer_Notification_Manual_Trial_Expiration
|
||||||
|
&& $emails['WCS_Email_Customer_Notification_Auto_Trial_Expiration'] instanceof WCS_Email_Customer_Notification_Auto_Trial_Expiration
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$notification = null;
|
||||||
|
switch ( current_action() ) {
|
||||||
|
case 'woocommerce_scheduled_subscription_customer_notification_renewal':
|
||||||
|
$subscription = wcs_get_subscription( $subscription_id );
|
||||||
|
if ( $subscription->is_manual() ) {
|
||||||
|
$notification = $emails['WCS_Email_Customer_Notification_Manual_Renewal'];
|
||||||
|
} else {
|
||||||
|
$notification = $emails['WCS_Email_Customer_Notification_Auto_Renewal'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'woocommerce_scheduled_subscription_customer_notification_trial_expiration':
|
||||||
|
$subscription = wcs_get_subscription( $subscription_id );
|
||||||
|
if ( $subscription->is_manual() ) {
|
||||||
|
$notification = $emails['WCS_Email_Customer_Notification_Manual_Trial_Expiration'];
|
||||||
|
} else {
|
||||||
|
$notification = $emails['WCS_Email_Customer_Notification_Auto_Trial_Expiration'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'woocommerce_scheduled_subscription_customer_notification_expiration':
|
||||||
|
$notification = $emails['WCS_Email_Customer_Notification_Subscription_Expiration'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $notification ) {
|
||||||
|
$notification->trigger( $subscription_id );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the notifications feature enabled?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function notifications_globally_enabled() {
|
||||||
|
return ( 'yes' === get_option( WC_Subscriptions_Admin::$option_prefix . self::$switch_setting_string )
|
||||||
|
&& get_option( WC_Subscriptions_Admin::$option_prefix . self::$offset_setting_string ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the emails be sent out?
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function should_send_notification() {
|
||||||
|
$notification_enabled = true;
|
||||||
|
|
||||||
|
if ( WCS_Staging::is_duplicate_site() ) {
|
||||||
|
$notification_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowed_env_types = [
|
||||||
|
'production',
|
||||||
|
];
|
||||||
|
if ( ! in_array( wp_get_environment_type(), $allowed_env_types, true ) ) {
|
||||||
|
$notification_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Customer notifications are disabled in the settings by a global switch, or there is no offset set, don't send notifications.
|
||||||
|
if ( ! self::notifications_globally_enabled() ) {
|
||||||
|
$notification_enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $notification_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds actions to the admin edit subscriptions page.
|
||||||
|
*
|
||||||
|
* @param array $actions An array of available actions
|
||||||
|
* @return array An array of updated actions
|
||||||
|
*/
|
||||||
|
public static function add_notification_actions( $actions ) {
|
||||||
|
global $theorder;
|
||||||
|
|
||||||
|
if ( ! self::notifications_globally_enabled() ) {
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! wcs_is_subscription( $theorder ) ) {
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $theorder->has_status( [ 'active', 'on-hold', 'pending-cancel' ] ) ) {
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
$valid_notifications = WCS_Action_Scheduler_Customer_Notifications::get_valid_notifications( $theorder );
|
||||||
|
|
||||||
|
if ( in_array( 'trial_end', $valid_notifications, true ) ) {
|
||||||
|
$actions['wcs_customer_notification_free_trial_expiration'] = esc_html__( 'Send trial is ending notification', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( in_array( 'end', $valid_notifications, true ) ) {
|
||||||
|
$actions['wcs_customer_notification_subscription_expiration'] = esc_html__( 'Send upcoming subscription expiration notification', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( in_array( 'next_payment', $valid_notifications, true ) ) {
|
||||||
|
$actions['wcs_customer_notification_renewal'] = esc_html__( 'Send upcoming renewal notification', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the subscription notification setting.
|
||||||
|
*
|
||||||
|
* @param array $settings Subscriptions settings.
|
||||||
|
* @return array Subscriptions settings.
|
||||||
|
*/
|
||||||
|
public static function add_settings( $settings ) {
|
||||||
|
|
||||||
|
$notification_settings = [
|
||||||
|
[
|
||||||
|
'name' => __( 'Customer Notifications', 'woocommerce-subscriptions' ),
|
||||||
|
'type' => 'title',
|
||||||
|
'id' => WC_Subscriptions_Admin::$option_prefix . '_customer_notifications',
|
||||||
|
/* translators: Link to WC Settings > Email. */
|
||||||
|
'desc' => sprintf( __( 'To enable/disable individual notifications and customize templates, visit the <a href="%s">Email settings</a>.', 'woocommerce-subscriptions' ), admin_url( 'admin.php?page=wc-settings&tab=email' ) ),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => __( 'Enable Reminders', 'woocommerce-subscriptions' ),
|
||||||
|
'desc' => __( 'Send notification emails to customers for subscription renewals and expirations.', 'woocommerce-subscriptions' ),
|
||||||
|
'tip' => '',
|
||||||
|
'id' => WC_Subscriptions_Admin::$option_prefix . self::$switch_setting_string,
|
||||||
|
'desc_tip' => false,
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'default' => 'no',
|
||||||
|
'autoload' => false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'name' => __( 'Reminder Timing', 'woocommerce-subscriptions' ),
|
||||||
|
'desc' => __( 'How long before the event should the notification be sent.', 'woocommerce-subscriptions' ),
|
||||||
|
'tip' => '',
|
||||||
|
'id' => WC_Subscriptions_Admin::$option_prefix . self::$offset_setting_string,
|
||||||
|
'desc_tip' => true,
|
||||||
|
'type' => 'relative_date_selector',
|
||||||
|
'placeholder' => __( 'N/A', 'woocommerce-subscriptions' ),
|
||||||
|
'default' => [
|
||||||
|
'number' => '3',
|
||||||
|
'unit' => 'days',
|
||||||
|
],
|
||||||
|
'autoload' => false,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'type' => 'sectionend',
|
||||||
|
'id' => WC_Subscriptions_Admin::$option_prefix . '_customer_notifications',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
WC_Subscriptions_Admin::insert_setting_after( $settings, WC_Subscriptions_Admin::$option_prefix . '_miscellaneous', $notification_settings, 'multiple_settings', 'sectionend' );
|
||||||
|
return $settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maybe add an admin notice to inform the store manager about the existance of the notifications feature.
|
||||||
|
*/
|
||||||
|
public static function maybe_add_admin_notice() {
|
||||||
|
|
||||||
|
// If the notifications feature is enabled, don't show the notice.
|
||||||
|
if ( self::notifications_globally_enabled() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent showing the notice on the Subscriptions settings page.
|
||||||
|
if ( isset( $_GET['page'], $_GET['tab'] ) && 'wc-settings' === $_GET['page'] && 'subscriptions' === $_GET['tab'] ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$option_name = 'wcs_hide_customer_notifications_notice';
|
||||||
|
$nonce = '_wcsnonce';
|
||||||
|
$action = 'wcs_hide_customer_notifications_notice_action';
|
||||||
|
|
||||||
|
// First, check if the notice is being dismissed.
|
||||||
|
$nonce_argument = sanitize_text_field( wp_unslash( $_GET[ $nonce ] ?? '' ) );
|
||||||
|
if ( isset( $_GET[ $action ], $nonce_argument ) && wp_verify_nonce( $nonce_argument, $action ) ) {
|
||||||
|
update_option( $option_name, 'yes' );
|
||||||
|
wp_safe_redirect( remove_query_arg( [ $action, $nonce ] ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( 'yes' === get_option( $option_name ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$admin_notice = new WCS_Admin_Notice( 'notice', array(), wp_nonce_url( add_query_arg( $action, 'dismiss' ), $action, $nonce ) );
|
||||||
|
$notice_title = __( 'WooCommerce Subscriptions: Introducing customer email notifications!', 'woocommerce-subscriptions' );
|
||||||
|
$notice_content = __( 'You can now send email notifications for subscription renewals, expirations, and free trials. Go to the "Customer Notifications" settings section to configure when your customers receive these important updates.', 'woocommerce-subscriptions' );
|
||||||
|
$html_content = sprintf( '<p class="main"><strong>%1$s</strong></p><p>%2$s</p>', $notice_title, $notice_content );
|
||||||
|
$admin_notice->set_html_content( $html_content );
|
||||||
|
$admin_notice->set_actions(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => __( 'Manage settings', 'woocommerce-subscriptions' ),
|
||||||
|
'url' => admin_url( 'admin.php?page=wc-settings&tab=subscriptions' ),
|
||||||
|
'class' => 'button button-primary',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => __( 'Learn more', 'woocommerce-subscriptions' ),
|
||||||
|
'url' => 'https://woocommerce.com/document/subscriptions/subscriptions-notifications/',
|
||||||
|
'class' => 'button',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$admin_notice->display();
|
||||||
|
}
|
||||||
|
}
|
@@ -294,6 +294,31 @@ class WC_Subscriptions_Email {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the subscription details table.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription[] $subscriptions List of subscriptions. Also accepts a single subscription.
|
||||||
|
* @param WC_Order|null $order The order related to the subscription - defaults to parent order.
|
||||||
|
* @param bool $sent_to_admin Whether the email is sent to admin - defaults to false.
|
||||||
|
* @param bool $plain_text Whether the email should use plain text templates - defaults to false.
|
||||||
|
* @param bool $skip_my_account_link Whether to skip displaying the My Account link - defaults to false.
|
||||||
|
*/
|
||||||
|
public static function subscription_details( $subscriptions, $order = null, $sent_to_admin = false, $plain_text = false, $skip_my_account_link = false ) {
|
||||||
|
$template = ( $plain_text ) ? 'emails/plain/subscription-info.php' : 'emails/subscription-info.php';
|
||||||
|
|
||||||
|
wc_get_template(
|
||||||
|
$template,
|
||||||
|
array(
|
||||||
|
'order' => ! $order ? $subscription->get_parent() : $order,
|
||||||
|
'subscriptions' => is_array( $subscriptions ) ? $subscriptions : [ $subscriptions ],
|
||||||
|
'is_admin_email' => $sent_to_admin,
|
||||||
|
'skip_my_account_link' => $skip_my_account_link,
|
||||||
|
),
|
||||||
|
'',
|
||||||
|
WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detach WC transactional emails from a specific hook.
|
* Detach WC transactional emails from a specific hook.
|
||||||
*
|
*
|
||||||
|
@@ -2028,16 +2028,13 @@ class WC_Subscriptions_Manager {
|
|||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0.2
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0.2
|
||||||
*/
|
*/
|
||||||
public static function maybe_process_failed_renewal_for_repair( $subscription_id ) {
|
public static function maybe_process_failed_renewal_for_repair( $subscription_id ) {
|
||||||
|
$subscription = wcs_get_subscription( $subscription_id );
|
||||||
if ( 'true' == get_post_meta( $subscription_id, '_wcs_repaired_2_0_2_needs_failed_payment', true ) ) {
|
if ( 'true' === $subscription->get_meta( '_wcs_repaired_2_0_2_needs_failed_payment', true ) ) {
|
||||||
|
|
||||||
$subscription = wcs_get_subscription( $subscription_id );
|
|
||||||
|
|
||||||
// Always put the subscription on hold in case something goes wrong while trying to process renewal
|
// Always put the subscription on hold in case something goes wrong while trying to process renewal
|
||||||
$subscription->update_status( 'on-hold', _x( 'Subscription renewal payment due:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) );
|
$subscription->update_status( 'on-hold', _x( 'Subscription renewal payment due:', 'used in order note as reason for why subscription status changed', 'woocommerce-subscriptions' ) );
|
||||||
|
|
||||||
// Create a renewal order to record the failed payment which can then be used by the customer to reactivate the subscription
|
// Create a renewal order to record the failed payment which can then be used by the customer to reactivate the subscription
|
||||||
$renewal_order = wcs_create_renewal_order( $subscription );
|
wcs_create_renewal_order( $subscription );
|
||||||
|
|
||||||
// Mark the payment as failed so the customer can login to fix up the failed payment
|
// Mark the payment as failed so the customer can login to fix up the failed payment
|
||||||
$subscription->payment_failed();
|
$subscription->payment_failed();
|
||||||
|
@@ -741,7 +741,7 @@ class WC_Subscriptions_Order {
|
|||||||
*
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.5
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.5
|
||||||
*/
|
*/
|
||||||
public static function add_sub_info_email( $order, $is_admin_email, $plaintext = false ) {
|
public static function add_sub_info_email( $order, $is_admin_email, $plaintext = false, $skip_my_account_link = false ) {
|
||||||
|
|
||||||
$subscriptions = wcs_get_subscriptions_for_order( $order, array( 'order_type' => 'any' ) );
|
$subscriptions = wcs_get_subscriptions_for_order( $order, array( 'order_type' => 'any' ) );
|
||||||
|
|
||||||
@@ -753,9 +753,10 @@ class WC_Subscriptions_Order {
|
|||||||
wc_get_template(
|
wc_get_template(
|
||||||
$template,
|
$template,
|
||||||
array(
|
array(
|
||||||
'order' => $order,
|
'order' => $order,
|
||||||
'subscriptions' => $subscriptions,
|
'subscriptions' => $subscriptions,
|
||||||
'is_admin_email' => $is_admin_email,
|
'is_admin_email' => $is_admin_email,
|
||||||
|
'skip_my_account_link' => $skip_my_account_link,
|
||||||
),
|
),
|
||||||
'',
|
'',
|
||||||
$template_base
|
$template_base
|
||||||
|
@@ -126,13 +126,10 @@ class WC_Subscriptions_Product {
|
|||||||
$contains_subscription = false;
|
$contains_subscription = false;
|
||||||
|
|
||||||
foreach ( $grouped_product->get_children() as $child_product_id ) {
|
foreach ( $grouped_product->get_children() as $child_product_id ) {
|
||||||
|
$child_product = wc_get_product( $child_product_id );
|
||||||
if ( self::is_subscription( $child_product_id ) ) {
|
if ( self::is_subscription( $child_product_id ) ) {
|
||||||
|
|
||||||
$contains_subscription = true;
|
$contains_subscription = true;
|
||||||
|
|
||||||
$child_product = wc_get_product( $child_product_id );
|
|
||||||
|
|
||||||
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
|
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
|
||||||
$child_price = 'incl' == $tax_display_mode ? wcs_get_price_including_tax( $child_product, array( 'price' => $child_product->get_price() ) ) : wcs_get_price_excluding_tax( $child_product, array( 'price' => $child_product->get_price() ) );
|
$child_price = 'incl' == $tax_display_mode ? wcs_get_price_including_tax( $child_product, array( 'price' => $child_product->get_price() ) ) : wcs_get_price_excluding_tax( $child_product, array( 'price' => $child_product->get_price() ) );
|
||||||
$sign_up_fee = 'incl' == $tax_display_mode ? wcs_get_price_including_tax( $child_product, array( 'price' => self::get_sign_up_fee( $child_product ) ) ) : wcs_get_price_excluding_tax( $child_product, array( 'price' => self::get_sign_up_fee( $child_product ) ) );
|
$sign_up_fee = 'incl' == $tax_display_mode ? wcs_get_price_including_tax( $child_product, array( 'price' => self::get_sign_up_fee( $child_product ) ) ) : wcs_get_price_excluding_tax( $child_product, array( 'price' => self::get_sign_up_fee( $child_product ) ) );
|
||||||
@@ -146,11 +143,8 @@ class WC_Subscriptions_Product {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$child_prices[] = $child_price;
|
$child_prices[] = $child_price;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
$child_prices[] = $child_product->get_price();
|
||||||
$child_prices[] = get_post_meta( $child_product_id, '_price', true );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -934,7 +928,8 @@ class WC_Subscriptions_Product {
|
|||||||
$value = wc_clean( $data['value'] );
|
$value = wc_clean( $data['value'] );
|
||||||
|
|
||||||
foreach ( $variation_ids as $variation_id ) {
|
foreach ( $variation_ids as $variation_id ) {
|
||||||
$subscription_price = get_post_meta( $variation_id, '_subscription_price', true );
|
$variation = wc_get_product( $variation_id );
|
||||||
|
$subscription_price = $variation->get_meta( '_subscription_price', true );
|
||||||
|
|
||||||
if ( '%' === substr( $value, -1 ) ) {
|
if ( '%' === substr( $value, -1 ) ) {
|
||||||
$percent = wc_format_decimal( substr( $value, 0, -1 ) );
|
$percent = wc_format_decimal( substr( $value, 0, -1 ) );
|
||||||
|
@@ -263,7 +263,8 @@ class WC_Subscriptions_Synchroniser {
|
|||||||
if ( self::is_syncing_enabled() ) {
|
if ( self::is_syncing_enabled() ) {
|
||||||
|
|
||||||
// Set month as the default billing period
|
// Set month as the default billing period
|
||||||
if ( ! $subscription_period = get_post_meta( $post->ID, '_subscription_period', true ) ) {
|
$subscription_period = get_post_meta( $post->ID, '_subscription_period', true );
|
||||||
|
if ( ! $subscription_period ) {
|
||||||
$subscription_period = 'month';
|
$subscription_period = 'month';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -857,6 +858,7 @@ class WC_Subscriptions_Synchroniser {
|
|||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
*/
|
*/
|
||||||
public static function products_first_renewal_payment_time( $first_renewal_timestamp, $product_id, $from_date, $timezone ) {
|
public static function products_first_renewal_payment_time( $first_renewal_timestamp, $product_id, $from_date, $timezone ) {
|
||||||
|
$unmodified_first_renewal_timestamp = $first_renewal_timestamp;
|
||||||
|
|
||||||
if ( self::is_product_synced( $product_id ) ) {
|
if ( self::is_product_synced( $product_id ) ) {
|
||||||
|
|
||||||
@@ -871,7 +873,18 @@ class WC_Subscriptions_Synchroniser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $first_renewal_timestamp;
|
/**
|
||||||
|
* Filter the first renewal payment date string for a product.
|
||||||
|
*
|
||||||
|
* @since 7.7.0
|
||||||
|
*
|
||||||
|
* @param int $first_renewal_timestamp The timestamp of the first renewal payment date.
|
||||||
|
* @param int $product_id The product ID.
|
||||||
|
* @param string $from_date The date to calculate the first payment from in GMT/UTC timezone.
|
||||||
|
* @param string $timezone The timezone to use for the first payment date.
|
||||||
|
* @param int $unmodified_first_renewal_timestamp The unmodified timestamp of the first renewal payment date.
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_subscriptions_synced_first_renewal_payment_timestamp', $first_renewal_timestamp, $product_id, $from_date, $timezone, $unmodified_first_renewal_timestamp );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1495,10 +1508,12 @@ class WC_Subscriptions_Synchroniser {
|
|||||||
_deprecated_function( __METHOD__, '2.0', __CLASS__ . '::subscription_contains_synced_product()' );
|
_deprecated_function( __METHOD__, '2.0', __CLASS__ . '::subscription_contains_synced_product()' );
|
||||||
|
|
||||||
if ( is_object( $order_id ) ) {
|
if ( is_object( $order_id ) ) {
|
||||||
$order_id = wcs_get_objects_property( $order_id, 'id' );
|
$order = $order_id;
|
||||||
|
} else {
|
||||||
|
$order = wc_get_order( $order_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'true' == get_post_meta( $order_id, '_order_contains_synced_subscription', true );
|
return 'true' === $order->get_meta( '_order_contains_synced_subscription', true );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -79,6 +79,9 @@ class WC_Subscriptions_Tracker {
|
|||||||
'allow_zero_initial_order_without_payment_method' => get_option( WC_Subscriptions_Admin::$option_prefix . '_zero_initial_payment_requires_payment' ),
|
'allow_zero_initial_order_without_payment_method' => get_option( WC_Subscriptions_Admin::$option_prefix . '_zero_initial_payment_requires_payment' ),
|
||||||
'drip_downloadable_content_on_renewal' => get_option( WC_Subscriptions_Admin::$option_prefix . '_drip_downloadable_content_on_renewal' ),
|
'drip_downloadable_content_on_renewal' => get_option( WC_Subscriptions_Admin::$option_prefix . '_drip_downloadable_content_on_renewal' ),
|
||||||
'enable_retry' => get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_retry' ),
|
'enable_retry' => get_option( WC_Subscriptions_Admin::$option_prefix . '_enable_retry' ),
|
||||||
|
|
||||||
|
// Notifications
|
||||||
|
'enable_notification_reminders' => get_option( WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$switch_setting_string, 'no' ),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
531
vendor/woocommerce/subscriptions-core/includes/class-wcs-action-scheduler-customer-notifications.php
vendored
Normal file
531
vendor/woocommerce/subscriptions-core/includes/class-wcs-action-scheduler-customer-notifications.php
vendored
Normal file
@@ -0,0 +1,531 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scheduler for subscription notifications that uses the Action Scheduler
|
||||||
|
*
|
||||||
|
* @class WCS_Action_Scheduler_Customer_Notifications
|
||||||
|
* @version 7.7.0
|
||||||
|
* @package WooCommerce Subscriptions/Classes
|
||||||
|
* @category Class
|
||||||
|
*/
|
||||||
|
class WCS_Action_Scheduler_Customer_Notifications extends WCS_Scheduler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int Time offset (in whole seconds) between the notification and the action it's notifying about.
|
||||||
|
*/
|
||||||
|
protected $time_offset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array|string[] Notifications scheduled by this class.
|
||||||
|
*
|
||||||
|
* Just for reference.
|
||||||
|
*/
|
||||||
|
protected static $notification_actions = [
|
||||||
|
'woocommerce_scheduled_subscription_customer_notification_trial_expiration',
|
||||||
|
'woocommerce_scheduled_subscription_customer_notification_expiration',
|
||||||
|
'woocommerce_scheduled_subscription_customer_notification_renewal',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of Action Scheduler group used for customer notification actions.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected static $notifications_as_group = 'wcs_customer_notifications';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$setting_option = get_option(
|
||||||
|
WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$offset_setting_string,
|
||||||
|
[
|
||||||
|
'number' => 3,
|
||||||
|
'unit' => 'days',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$this->set_time_offset( self::convert_offset_to_seconds( $setting_option ) );
|
||||||
|
|
||||||
|
add_action( 'woocommerce_before_subscription_object_save', [ $this, 'update_notifications' ], 10, 2 );
|
||||||
|
|
||||||
|
add_action( 'update_option_' . WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$offset_setting_string, [ $this, 'set_time_offset_from_option' ], 5, 3 );
|
||||||
|
add_action( 'add_option_' . WC_Subscriptions_Admin::$option_prefix . WC_Subscriptions_Email_Notifications::$offset_setting_string, [ $this, 'set_time_offset_from_option' ], 5, 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the subscription period is too short to send a renewal notification.
|
||||||
|
*
|
||||||
|
* @param $subscription
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function is_subscription_period_too_short( $subscription ) {
|
||||||
|
$period = $subscription->get_billing_period();
|
||||||
|
$interval = $subscription->get_billing_interval();
|
||||||
|
|
||||||
|
// By default, there are no shorter periods than days in WCS, so we ignore hours, minutes, etc.
|
||||||
|
if ( $interval <= 2 && 'day' === $period ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return time offset for notifications for given subscription.
|
||||||
|
*
|
||||||
|
* Generally, there is one offset for all subscriptions, but there's a filter.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
* @param string $notification_type
|
||||||
|
*
|
||||||
|
* @return mixed|null
|
||||||
|
*/
|
||||||
|
public function get_time_offset( $subscription, $notification_type ) {
|
||||||
|
/**
|
||||||
|
* Offset between a subscription event and related notification.
|
||||||
|
*
|
||||||
|
* @since 7.7.0
|
||||||
|
*
|
||||||
|
* @param int $time_offset In seconds
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
* @param string $notification_type Can be 'trial_end', 'next_payment' or 'end'.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
return apply_filters( 'woocommerce_subscription_customer_notification_time_offset', $this->time_offset, $subscription, $notification_type );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General time offset setter.
|
||||||
|
*
|
||||||
|
* @param int $time_offset In seconds
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set_time_offset( $time_offset ) {
|
||||||
|
$this->time_offset = $time_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the offset based on new value set in the option.
|
||||||
|
*
|
||||||
|
* @param $_ Unused parameter.
|
||||||
|
* @param $new_option_value
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set_time_offset_from_option( $_, $new_option_value ) {
|
||||||
|
$this->time_offset = self::convert_offset_to_seconds( $new_option_value );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate time offset in seconds from the settings array.
|
||||||
|
*
|
||||||
|
* @param array $offset Format: [ 'number' => 3, 'unit' => 'days' ]
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected static function convert_offset_to_seconds( $offset ) {
|
||||||
|
$default_offset = 3 * DAY_IN_SECONDS;
|
||||||
|
|
||||||
|
if ( ! isset( $offset['unit'] ) || ! isset( $offset['number'] ) ) {
|
||||||
|
return $default_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( $offset['unit'] ) {
|
||||||
|
case 'days':
|
||||||
|
return ( $offset['number'] * DAY_IN_SECONDS );
|
||||||
|
case 'weeks':
|
||||||
|
return ( $offset['number'] * WEEK_IN_SECONDS );
|
||||||
|
case 'months':
|
||||||
|
return ( $offset['number'] * MONTH_IN_SECONDS );
|
||||||
|
case 'years':
|
||||||
|
return ( $offset['number'] * YEAR_IN_SECONDS );
|
||||||
|
default:
|
||||||
|
return $default_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maybe schedule a notification action for given subscription and timestamp.
|
||||||
|
*
|
||||||
|
* Will *not* schedule notification if:
|
||||||
|
* - the notifications are globally disabled,
|
||||||
|
* - the subscription isn't active/pending-cancel,
|
||||||
|
* - the subscription's billing cycle is less than 3 days,
|
||||||
|
* - there is already the same action scheduled for the same subscription and time.
|
||||||
|
*
|
||||||
|
* If only the time differs, the previous scheduled action will be unscheduled and a new one will replace it.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription Subscription to schedule the action for.
|
||||||
|
* @param string $action Action ID to schedule.
|
||||||
|
* @param int $timestamp Time to schedule the notification for.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function maybe_schedule_notification( $subscription, $action, $timestamp ) {
|
||||||
|
if ( ! WC_Subscriptions_Email_Notifications::notifications_globally_enabled() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $subscription->has_status( [ 'active', 'pending-cancel' ] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( self::is_subscription_period_too_short( $subscription ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$action_args = self::get_action_args( $subscription );
|
||||||
|
|
||||||
|
$next_scheduled = as_next_scheduled_action( $action, $action_args, self::$notifications_as_group );
|
||||||
|
|
||||||
|
if ( $timestamp === $next_scheduled ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->unschedule_actions( $action, $action_args );
|
||||||
|
|
||||||
|
// Only reschedule if it's in the future
|
||||||
|
if ( $timestamp <= time() ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
as_schedule_single_action( $timestamp, $action, $action_args, self::$notifications_as_group );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract time offset from given datetime based on the settings and subscription properties and return resulting timestamp.
|
||||||
|
*
|
||||||
|
* @param string $datetime
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
* @param string $notification_type Can be 'trial_end', 'next_payment' or 'end'.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected function subtract_time_offset( $datetime, $subscription, $notification_type ) {
|
||||||
|
$dt = new DateTime( $datetime, new DateTimeZone( 'UTC' ) );
|
||||||
|
|
||||||
|
return $dt->getTimestamp() - $this->get_time_offset( $subscription, $notification_type );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification action name based on the date type.
|
||||||
|
*
|
||||||
|
* @param string $date_type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function get_action_from_date_type( $date_type ) {
|
||||||
|
$action = '';
|
||||||
|
|
||||||
|
switch ( $date_type ) {
|
||||||
|
case 'trial_end':
|
||||||
|
$action = 'woocommerce_scheduled_subscription_customer_notification_trial_expiration';
|
||||||
|
break;
|
||||||
|
case 'next_payment':
|
||||||
|
$action = 'woocommerce_scheduled_subscription_customer_notification_renewal';
|
||||||
|
break;
|
||||||
|
case 'end':
|
||||||
|
$action = 'woocommerce_scheduled_subscription_customer_notification_expiration';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update notifications when subscription gets updated.
|
||||||
|
*
|
||||||
|
* To make batch processing easier, we need to handle the following use case:
|
||||||
|
* 1. Subscription S1 gets updated.
|
||||||
|
* 2. Notification config gets updated, a batch to fix all subscriptions is started and processes all subscriptions
|
||||||
|
* with update time before the config got updated.
|
||||||
|
* 3. Subscription S1 gets updated before it gets processed by the batch process.
|
||||||
|
*
|
||||||
|
* Thus, we update notifications for all subscriptions that are being updated after notification config change time
|
||||||
|
* and which have their update time before that.
|
||||||
|
*
|
||||||
|
* As this gets called on Subscription save, the modification timestamp should be updated, too, and thus
|
||||||
|
* the currently updated subscription no longer needs to be processed by the batch process.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
* @param $subscription_data_store
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function update_notifications( $subscription, $subscription_data_store ) {
|
||||||
|
if ( ! $subscription->has_status( 'active' ) && ! $subscription->has_status( 'pending-cancel' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here, we need the 'old' update timestamp for comparison, so can't use get_date_modified() method.
|
||||||
|
$subscription_update_time_raw = array_key_exists( 'date_modified', $subscription->get_data() ) ? $subscription->get_data()['date_modified'] : $subscription->get_date_created();
|
||||||
|
if ( ! $subscription_update_time_raw ) {
|
||||||
|
$subscription_update_utc_timestamp = 0;
|
||||||
|
} else {
|
||||||
|
$subscription_update_time_raw->setTimezone( new DateTimeZone( 'UTC' ) );
|
||||||
|
$subscription_update_utc_timestamp = $subscription_update_time_raw->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
$notification_settings_update_utc_timestamp = get_option( 'wcs_notification_settings_update_time', 0 );
|
||||||
|
|
||||||
|
if ( $subscription_update_utc_timestamp < $notification_settings_update_utc_timestamp ) {
|
||||||
|
$this->schedule_all_notifications( $subscription );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a notification with given type for given subscription.
|
||||||
|
*
|
||||||
|
* Date/time is determined automatically based on notification type, dates stored on the subscription,
|
||||||
|
* and offset WCS_Action_Scheduler_Customer_Notifications::$time_offset.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
* @param string $notification_type
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function schedule_notification( $subscription, $notification_type ) {
|
||||||
|
$action_name = self::get_action_from_date_type( $notification_type );
|
||||||
|
|
||||||
|
$event_date = $subscription->get_date( $notification_type );
|
||||||
|
$timestamp = $this->subtract_time_offset( $event_date, $subscription, $notification_type );
|
||||||
|
|
||||||
|
$this->maybe_schedule_notification(
|
||||||
|
$subscription,
|
||||||
|
$action_name,
|
||||||
|
$timestamp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule all notifications for a subscription based on the dates defined on the subscription.
|
||||||
|
*
|
||||||
|
* Which notifications are needed for the subscription is determined by \WCS_Action_Scheduler_Customer_Notifications::get_valid_notifications.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function schedule_all_notifications( $subscription ) {
|
||||||
|
$valid_notifications = self::get_valid_notifications( $subscription );
|
||||||
|
$actual_notifications = $this->get_notifications( $subscription );
|
||||||
|
|
||||||
|
// Unschedule notifications that aren't valid for this subscription.
|
||||||
|
$notifications_to_unschedule = array_diff( $actual_notifications, $valid_notifications );
|
||||||
|
foreach ( $notifications_to_unschedule as $notification_type ) {
|
||||||
|
$this->unschedule_actions( self::get_action_from_date_type( $notification_type ), self::get_action_args( $subscription ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule/check scheduling for valid notifications.
|
||||||
|
foreach ( $valid_notifications as $notification_type ) {
|
||||||
|
$this->schedule_notification( $subscription, $notification_type );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set which date types are affecting the notifications.
|
||||||
|
*
|
||||||
|
* Currently, only trial_end, end and next_payment are being used.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function set_date_types_to_schedule() {
|
||||||
|
$this->date_types_to_schedule = [
|
||||||
|
'trial_end',
|
||||||
|
'next_payment',
|
||||||
|
'end',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule notifications if the date has changed.
|
||||||
|
*
|
||||||
|
* @param object $subscription An instance of a WC_Subscription object
|
||||||
|
* @param string $date_type Can be 'trial_end', 'next_payment', 'payment_retry', 'end', 'end_of_prepaid_term' or a custom date type
|
||||||
|
* @param string $datetime A MySQL formatted date/time string in the GMT/UTC timezone.
|
||||||
|
*/
|
||||||
|
public function update_date( $subscription, $date_type, $datetime ) {
|
||||||
|
if ( in_array( $date_type, $this->get_date_types_to_schedule(), true ) ) {
|
||||||
|
$this->schedule_all_notifications( $subscription );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule notifications if the date has been deleted.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription An instance of a WC_Subscription object
|
||||||
|
* @param string $date_type Can be 'trial_end', 'next_payment', 'end', 'end_of_prepaid_term' or a custom date type
|
||||||
|
*/
|
||||||
|
public function delete_date( $subscription, $date_type ) {
|
||||||
|
$action = $this->get_action_from_date_type( $date_type );
|
||||||
|
if ( $action ) {
|
||||||
|
$this->unschedule_actions( $action, self::get_action_args( $subscription ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->schedule_all_notifications( $subscription );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unschedule all notifications for a subscription.
|
||||||
|
*
|
||||||
|
* @param object $subscription An instance of a WC_Subscription object
|
||||||
|
* @param array $exceptions Array of notification actions to not unschedule
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function unschedule_all_notifications( $subscription = null, $exceptions = [] ) {
|
||||||
|
foreach ( self::$notification_actions as $action ) {
|
||||||
|
if ( in_array( $action, $exceptions, true ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->unschedule_actions( $action, self::get_action_args( $subscription ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a subscription's status is updated, maybe schedule an event
|
||||||
|
*
|
||||||
|
* @param object $subscription An instance of a WC_Subscription object
|
||||||
|
* @param string $new_status New subscription status
|
||||||
|
* @param string $old_status Previous subscription status
|
||||||
|
*/
|
||||||
|
public function update_status( $subscription, $new_status, $old_status ) {
|
||||||
|
|
||||||
|
switch ( $new_status ) {
|
||||||
|
case 'active':
|
||||||
|
// Schedule new notifications (will also unschedule unneeded ones).
|
||||||
|
$this->schedule_all_notifications( $subscription );
|
||||||
|
break;
|
||||||
|
case 'pending-cancel':
|
||||||
|
// Unschedule all except expiration notification.
|
||||||
|
$this->unschedule_all_notifications( $subscription, [ 'woocommerce_scheduled_subscription_customer_notification_expiration' ] );
|
||||||
|
break;
|
||||||
|
case 'on-hold':
|
||||||
|
case 'cancelled':
|
||||||
|
case 'switched':
|
||||||
|
case 'expired':
|
||||||
|
case 'trash':
|
||||||
|
$this->unschedule_all_notifications( $subscription );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the args to set on the scheduled action.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription|null $subscription An instance of WC_Subscription to get the hook for
|
||||||
|
*
|
||||||
|
* @return array Array of name => value pairs stored against the scheduled action.
|
||||||
|
*/
|
||||||
|
public static function get_action_args( $subscription ) {
|
||||||
|
if ( ! $subscription ) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$action_args = [ 'subscription_id' => $subscription->get_id() ];
|
||||||
|
|
||||||
|
return $action_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the args to set on the scheduled action.
|
||||||
|
*
|
||||||
|
* @param string $action_hook Name of event used as the hook for the scheduled action.
|
||||||
|
* @param array $action_args Array of name => value pairs stored against the scheduled action.
|
||||||
|
*/
|
||||||
|
protected function unschedule_actions( $action_hook, $action_args = [] ) {
|
||||||
|
as_unschedule_all_actions( $action_hook, $action_args, self::$notifications_as_group );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if given date for subscription is now or in the future.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription Subscription whose date is examined.
|
||||||
|
* @param string $date_type Date type to evaluate.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected static function is_date_in_the_future_or_now( $subscription, $date_type ) {
|
||||||
|
$dt = new DateTime( $subscription->get_date( $date_type ), new DateTimeZone( 'UTC' ) );
|
||||||
|
$timestamp = $dt->getTimestamp();
|
||||||
|
|
||||||
|
return $timestamp >= time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of notifications valid for given subscription based on the dates set on the subscription.
|
||||||
|
*
|
||||||
|
* This method doesn't take status into account. That's done in \WCS_Action_Scheduler_Customer_Notifications::update_status.
|
||||||
|
*
|
||||||
|
* Possible values in the array: 'end', 'trial_end', 'next_payment'.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function get_valid_notifications( $subscription ) {
|
||||||
|
$notifications = [];
|
||||||
|
|
||||||
|
if ( $subscription->get_date( 'end' ) && self::is_date_in_the_future_or_now( $subscription, 'end' ) ) {
|
||||||
|
$notifications[] = 'end';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $subscription->get_date( 'trial_end' ) && self::is_date_in_the_future_or_now( $subscription, 'trial_end' ) ) {
|
||||||
|
$notifications[] = 'trial_end';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $subscription->get_date( 'next_payment' ) ) {
|
||||||
|
|
||||||
|
// Renewal notification is only valid after the trial ended.
|
||||||
|
$trial_end = $subscription->get_date( 'trial_end' );
|
||||||
|
if ( $trial_end ) {
|
||||||
|
$trial_end_dt = new DateTime( $trial_end, new DateTimeZone( 'UTC' ) );
|
||||||
|
$trial_end_timestamp = $trial_end_dt->getTimestamp();
|
||||||
|
|
||||||
|
if ( $trial_end_timestamp < time() && self::is_date_in_the_future_or_now( $subscription, 'next_payment' ) ) {
|
||||||
|
$notifications[] = 'next_payment';
|
||||||
|
}
|
||||||
|
} elseif ( self::is_date_in_the_future_or_now( $subscription, 'next_payment' ) ) {
|
||||||
|
$notifications[] = 'next_payment';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of currently scheduled notifications for a subscription.
|
||||||
|
*
|
||||||
|
* Notifications are identified by the date type of the subscription.
|
||||||
|
* I.e. possible values are: 'end', 'trial_end' and 'next_payment'.
|
||||||
|
*
|
||||||
|
* @param $subscription
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_notifications( $subscription ) {
|
||||||
|
$notifications = [];
|
||||||
|
|
||||||
|
$date_types = $this->get_date_types_to_schedule();
|
||||||
|
|
||||||
|
foreach ( $date_types as $date_type ) {
|
||||||
|
$next_scheduled = as_next_scheduled_action(
|
||||||
|
self::get_action_from_date_type( $date_type ),
|
||||||
|
self::get_action_args( $subscription ),
|
||||||
|
self::$notifications_as_group
|
||||||
|
);
|
||||||
|
if ( $next_scheduled ) {
|
||||||
|
$notifications[] = $date_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $notifications;
|
||||||
|
}
|
||||||
|
}
|
572
vendor/woocommerce/subscriptions-core/includes/class-wcs-batch-processing-controller.php
vendored
Normal file
572
vendor/woocommerce/subscriptions-core/includes/class-wcs-batch-processing-controller.php
vendored
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This class is a helper intended to handle data processings that need to happen in batches in a deferred way.
|
||||||
|
* It abstracts away the nuances of (re)scheduling actions and dealing with errors.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* 1. Create a class that implements WCS_Batch_Processor.
|
||||||
|
*
|
||||||
|
* 2. Whenever there's data to be processed invoke the 'enqueue_processor' method in this class,
|
||||||
|
* passing the class name of the processor.
|
||||||
|
*
|
||||||
|
* That's it, processing will be performed in batches inside scheduled actions; enqueued processors will only
|
||||||
|
* be dequeued once they notify that no more items are left to process (or when `force_clear_all_processes` is invoked).
|
||||||
|
* Failed batches will be retried after a while.
|
||||||
|
*
|
||||||
|
* This is heavily inspired by core's version at Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Subscriptions
|
||||||
|
* @category Class
|
||||||
|
* @since 7.7.0
|
||||||
|
*/
|
||||||
|
class WCS_Batch_Processing_Controller {
|
||||||
|
/*
|
||||||
|
* Identifier of a "watchdog" action that will schedule a processing action
|
||||||
|
* for any processor that is enqueued but not yet scheduled
|
||||||
|
* (because it's been just enqueued or because it threw an error while processing a batch),
|
||||||
|
* that's one single action that reschedules itself continuously.
|
||||||
|
*/
|
||||||
|
const WATCHDOG_ACTION_NAME = 'wcs_schedule_pending_batch_processes';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Identifier of the action that will do the actual batch processing.
|
||||||
|
* There's one action per enqueued processor that will keep rescheduling itself
|
||||||
|
* as long as there are still pending items to process
|
||||||
|
* (except if there's an error that caused no items to be processed at all).
|
||||||
|
*/
|
||||||
|
const PROCESS_SINGLE_BATCH_ACTION_NAME = 'wcs_run_batch_process';
|
||||||
|
|
||||||
|
const ENQUEUED_PROCESSORS_OPTION_NAME = 'wcs_pending_batch_processes';
|
||||||
|
const ACTION_GROUP = 'wcs_batch_processes';
|
||||||
|
const LOGS_CONTEXT = 'wcs-batch-processing';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of failures per processor before it gets dequeued.
|
||||||
|
*/
|
||||||
|
const FAILING_PROCESS_MAX_ATTEMPTS_DEFAULT = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of WC_Logger class.
|
||||||
|
*
|
||||||
|
* @var \WC_Logger_Interface
|
||||||
|
*/
|
||||||
|
private $logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton instance.
|
||||||
|
*
|
||||||
|
* @var WCS_Batch_Processing_Controller
|
||||||
|
*/
|
||||||
|
private static $instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* Schedules the necessary actions to process batches.
|
||||||
|
*/
|
||||||
|
private function __construct() {
|
||||||
|
add_action(
|
||||||
|
self::WATCHDOG_ACTION_NAME,
|
||||||
|
function () {
|
||||||
|
$this->handle_watchdog_action();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
add_action(
|
||||||
|
self::PROCESS_SINGLE_BATCH_ACTION_NAME,
|
||||||
|
function ( $batch_process ) {
|
||||||
|
$this->process_next_batch_for_single_processor( $batch_process );
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
|
add_action(
|
||||||
|
'shutdown',
|
||||||
|
function () {
|
||||||
|
$this->remove_or_retry_failed_processors();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->logger = wc_get_logger();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the singleton instance of this class.
|
||||||
|
*
|
||||||
|
* @return WCS_Batch_Processing_Controller
|
||||||
|
*/
|
||||||
|
final public static function instance(): WCS_Batch_Processing_Controller {
|
||||||
|
if ( ! isset( self::$instance ) ) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueue a processor so that it will get batch processing requests from within scheduled actions.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Fully qualified class name of the processor, must implement `WCS_Batch_Processor`.
|
||||||
|
*/
|
||||||
|
public function enqueue_processor( string $processor_class_name ): void {
|
||||||
|
$pending_updates = $this->get_enqueued_processors();
|
||||||
|
if ( ! in_array( $processor_class_name, array_keys( $pending_updates ), true ) ) {
|
||||||
|
$pending_updates[] = $processor_class_name;
|
||||||
|
$this->set_enqueued_processors( $pending_updates );
|
||||||
|
}
|
||||||
|
$this->schedule_watchdog_action( false, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule the watchdog action.
|
||||||
|
*
|
||||||
|
* @param bool $with_delay Whether to delay the action execution. Should be true when rescheduling, false when enqueueing.
|
||||||
|
* @param bool $unique Whether to make the action unique.
|
||||||
|
*/
|
||||||
|
private function schedule_watchdog_action( bool $with_delay = false, bool $unique = false ): void {
|
||||||
|
$time = time();
|
||||||
|
if ( $with_delay ) {
|
||||||
|
/**
|
||||||
|
* Modify the delay interval for the batch processor's watchdog events.
|
||||||
|
*
|
||||||
|
* @since 7.7.0
|
||||||
|
*
|
||||||
|
* @param int $delay Time, in seconds, before the watchdog process will run. Defaults to 3600 (1 hour).
|
||||||
|
*/
|
||||||
|
$time += apply_filters( 'wcs_batch_processor_watchdog_delay_seconds', HOUR_IN_SECONDS );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! as_has_scheduled_action( self::WATCHDOG_ACTION_NAME ) ) {
|
||||||
|
as_schedule_single_action(
|
||||||
|
$time,
|
||||||
|
self::WATCHDOG_ACTION_NAME,
|
||||||
|
array(),
|
||||||
|
self::ACTION_GROUP,
|
||||||
|
$unique
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a processing action for all the processors that are enqueued but not scheduled
|
||||||
|
* (because they have just been enqueued, or because the processing for a batch failed).
|
||||||
|
*/
|
||||||
|
private function handle_watchdog_action(): void {
|
||||||
|
$pending_processes = $this->get_enqueued_processors();
|
||||||
|
if ( empty( $pending_processes ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ( $pending_processes as $process_name ) {
|
||||||
|
if ( ! $this->is_scheduled( $process_name ) ) {
|
||||||
|
$this->schedule_batch_processing( $process_name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->schedule_watchdog_action( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a batch for a single processor, and handle any required rescheduling or state cleanup.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Fully qualified class name of the processor.
|
||||||
|
*
|
||||||
|
* @throws \Exception If error occurred during batch processing.
|
||||||
|
*/
|
||||||
|
private function process_next_batch_for_single_processor( string $processor_class_name ): void {
|
||||||
|
if ( ! $this->is_enqueued( $processor_class_name ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$batch_processor = $this->get_processor_instance( $processor_class_name );
|
||||||
|
$error = $this->process_next_batch_for_single_processor_core( $batch_processor );
|
||||||
|
$still_pending = count( $batch_processor->get_next_batch_to_process( 1 ) ) > 0;
|
||||||
|
if ( ( $error instanceof \Exception ) ) {
|
||||||
|
// The batch processing failed and no items were processed:
|
||||||
|
// reschedule the processing with a delay, unless this is a repeatead failure.
|
||||||
|
if ( $this->is_consistently_failing( $batch_processor ) ) {
|
||||||
|
$this->log_consistent_failure( $batch_processor, $this->get_process_details( $batch_processor ) );
|
||||||
|
$this->remove_processor( $processor_class_name );
|
||||||
|
} else {
|
||||||
|
$this->schedule_batch_processing( $processor_class_name, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
if ( $still_pending ) {
|
||||||
|
$this->schedule_batch_processing( $processor_class_name );
|
||||||
|
} else {
|
||||||
|
$this->dequeue_processor( $processor_class_name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a batch for a single processor, updating state and logging any error.
|
||||||
|
*
|
||||||
|
* @param WCS_Batch_Processor $batch_processor Batch processor instance.
|
||||||
|
*
|
||||||
|
* @return null|\Exception Exception if error occurred, null otherwise.
|
||||||
|
*/
|
||||||
|
private function process_next_batch_for_single_processor_core( WCS_Batch_Processor $batch_processor ): ?\Exception {
|
||||||
|
$details = $this->get_process_details( $batch_processor );
|
||||||
|
$time_start = microtime( true );
|
||||||
|
$batch = $batch_processor->get_next_batch_to_process( $details['current_batch_size'] );
|
||||||
|
if ( empty( $batch ) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$batch_processor->process_batch( $batch );
|
||||||
|
$time_taken = microtime( true ) - $time_start;
|
||||||
|
$this->update_processor_state( $batch_processor, $time_taken );
|
||||||
|
} catch ( \Exception $exception ) {
|
||||||
|
$time_taken = microtime( true ) - $time_start;
|
||||||
|
$this->log_error( $exception, $batch_processor, $batch );
|
||||||
|
$this->update_processor_state( $batch_processor, $time_taken, $exception );
|
||||||
|
return $exception;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current state for a given enqueued processor.
|
||||||
|
*
|
||||||
|
* @param WCS_Batch_Processor $batch_processor Batch processor instance.
|
||||||
|
*
|
||||||
|
* @return array Current state for the processor, or a "blank" state if none exists yet.
|
||||||
|
*/
|
||||||
|
private function get_process_details( WCS_Batch_Processor $batch_processor ): array {
|
||||||
|
$defaults = array(
|
||||||
|
'total_time_spent' => 0,
|
||||||
|
'current_batch_size' => $batch_processor->get_default_batch_size(),
|
||||||
|
'last_error' => null,
|
||||||
|
'recent_failures' => 0,
|
||||||
|
'batch_first_failure' => null,
|
||||||
|
'batch_last_failure' => null,
|
||||||
|
);
|
||||||
|
|
||||||
|
$process_details = get_option( $this->get_processor_state_option_name( $batch_processor ) );
|
||||||
|
$process_details = wp_parse_args( is_array( $process_details ) ? $process_details : array(), $defaults );
|
||||||
|
|
||||||
|
return $process_details;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the name of the option where we will be saving state for a given processor.
|
||||||
|
*
|
||||||
|
* @param WCS_Batch_Processor|string $batch_processor Batch processor instance or class name.
|
||||||
|
*
|
||||||
|
* @return string Option name.
|
||||||
|
*/
|
||||||
|
private function get_processor_state_option_name( $batch_processor ): string {
|
||||||
|
$class_name = is_a( $batch_processor, WCS_Batch_Processor::class ) ? get_class( $batch_processor ) : $batch_processor;
|
||||||
|
$class_md5 = md5( $class_name );
|
||||||
|
// truncate the class name so we know that it will fit in the option name column along with md5 hash and prefix.
|
||||||
|
$class_name = substr( $class_name, 0, 140 );
|
||||||
|
return 'wcs_batch_' . $class_name . '_' . $class_md5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the state for a processor after a batch has completed processing.
|
||||||
|
*
|
||||||
|
* @param WCS_Batch_Processor $batch_processor Batch processor instance.
|
||||||
|
* @param float $time_taken Time take by the batch to complete processing.
|
||||||
|
* @param \Exception|null $last_error Exception object in processing the batch, if there was one.
|
||||||
|
*/
|
||||||
|
private function update_processor_state( WCS_Batch_Processor $batch_processor, float $time_taken, \Exception $last_error = null ): void {
|
||||||
|
$current_status = $this->get_process_details( $batch_processor );
|
||||||
|
$current_status['total_time_spent'] += $time_taken;
|
||||||
|
$current_status['last_error'] = null !== $last_error ? $last_error->getMessage() : null;
|
||||||
|
|
||||||
|
if ( null !== $last_error ) {
|
||||||
|
$current_status['recent_failures'] = ( $current_status['recent_failures'] ?? 0 ) + 1;
|
||||||
|
$current_status['batch_last_failure'] = current_time( 'mysql' );
|
||||||
|
|
||||||
|
if ( is_null( $current_status['batch_first_failure'] ) ) {
|
||||||
|
$current_status['batch_first_failure'] = $current_status['batch_last_failure'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$current_status['recent_failures'] = 0;
|
||||||
|
$current_status['batch_first_failure'] = null;
|
||||||
|
$current_status['batch_last_failure'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
update_option( $this->get_processor_state_option_name( $batch_processor ), $current_status, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the option where we store state for a given processor.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Fully qualified class name of the processor.
|
||||||
|
*/
|
||||||
|
private function clear_processor_state( string $processor_class_name ): void {
|
||||||
|
delete_option( $this->get_processor_state_option_name( $processor_class_name ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule a processing action for a single processor.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Fully qualified class name of the processor.
|
||||||
|
* @param bool $with_delay Whether to schedule the action for immediate execution or for later.
|
||||||
|
*/
|
||||||
|
private function schedule_batch_processing( string $processor_class_name, bool $with_delay = false ): void {
|
||||||
|
$time = $with_delay ? time() + MINUTE_IN_SECONDS : time();
|
||||||
|
as_schedule_single_action( $time, self::PROCESS_SINGLE_BATCH_ACTION_NAME, array( $processor_class_name ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a batch processing action is already scheduled for a given processor.
|
||||||
|
* Differs from `as_has_scheduled_action` in that this excludes actions in progress.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Fully qualified class name of the batch processor.
|
||||||
|
*
|
||||||
|
* @return bool True if a batch processing action is already scheduled for the processor.
|
||||||
|
*/
|
||||||
|
public function is_scheduled( string $processor_class_name ): bool {
|
||||||
|
return as_has_scheduled_action( self::PROCESS_SINGLE_BATCH_ACTION_NAME, array( $processor_class_name ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an instance of a processor given its class name.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Full class name of the batch processor.
|
||||||
|
*
|
||||||
|
* @return WCS_Batch_Processor Instance of batch processor for the given class.
|
||||||
|
* @throws \Exception If it's not possible to get an instance of the class.
|
||||||
|
*/
|
||||||
|
private function get_processor_instance( string $processor_class_name ): WCS_Batch_Processor {
|
||||||
|
if ( ! isset( $processor ) && class_exists( $processor_class_name ) ) {
|
||||||
|
// This is a fallback for when the batch processor is not registered in the container.
|
||||||
|
$processor = new $processor_class_name();
|
||||||
|
}
|
||||||
|
if ( ! is_a( $processor, WCS_Batch_Processor::class ) ) {
|
||||||
|
throw new \Exception( "Unable to initialize batch processor instance for $processor_class_name" );
|
||||||
|
}
|
||||||
|
return $processor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to get list of all the enqueued processors.
|
||||||
|
*
|
||||||
|
* @return array List (of string) of the class names of the enqueued processors.
|
||||||
|
*/
|
||||||
|
public function get_enqueued_processors(): array {
|
||||||
|
$enqueued_processors = get_option( self::ENQUEUED_PROCESSORS_OPTION_NAME, array() );
|
||||||
|
if ( ! is_array( $enqueued_processors ) ) {
|
||||||
|
$this->logger->error( 'Could not fetch list of processors. Clearing up queue.', array( 'source' => self::LOGS_CONTEXT ) );
|
||||||
|
delete_option( self::ENQUEUED_PROCESSORS_OPTION_NAME );
|
||||||
|
$enqueued_processors = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $enqueued_processors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeue a processor once it has no more items pending processing.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Full processor class name.
|
||||||
|
*/
|
||||||
|
private function dequeue_processor( string $processor_class_name ): void {
|
||||||
|
$pending_processes = $this->get_enqueued_processors();
|
||||||
|
if ( in_array( $processor_class_name, $pending_processes, true ) ) {
|
||||||
|
$this->clear_processor_state( $processor_class_name );
|
||||||
|
$pending_processes = array_diff( $pending_processes, array( $processor_class_name ) );
|
||||||
|
$this->set_enqueued_processors( $pending_processes );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to set the enqueued processor class names.
|
||||||
|
*
|
||||||
|
* @param array $processors List of full processor class names.
|
||||||
|
*/
|
||||||
|
private function set_enqueued_processors( array $processors ): void {
|
||||||
|
update_option( self::ENQUEUED_PROCESSORS_OPTION_NAME, $processors, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a particular processor is enqueued.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Fully qualified class name of the processor.
|
||||||
|
*
|
||||||
|
* @return bool True if the processor is enqueued.
|
||||||
|
*/
|
||||||
|
public function is_enqueued( string $processor_class_name ): bool {
|
||||||
|
return in_array( $processor_class_name, $this->get_enqueued_processors(), true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeue and de-schedule a processor instance so that it won't be processed anymore.
|
||||||
|
*
|
||||||
|
* @param string $processor_class_name Fully qualified class name of the processor.
|
||||||
|
* @return bool True if the processor has been dequeued, false if the processor wasn't enqueued (so nothing has been done).
|
||||||
|
*/
|
||||||
|
public function remove_processor( string $processor_class_name ): bool {
|
||||||
|
$enqueued_processors = $this->get_enqueued_processors();
|
||||||
|
if ( ! in_array( $processor_class_name, $enqueued_processors, true ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$enqueued_processors = array_diff( $enqueued_processors, array( $processor_class_name ) );
|
||||||
|
if ( empty( $enqueued_processors ) ) {
|
||||||
|
$this->force_clear_all_processes();
|
||||||
|
} else {
|
||||||
|
update_option( self::ENQUEUED_PROCESSORS_OPTION_NAME, $enqueued_processors, false );
|
||||||
|
as_unschedule_all_actions( self::PROCESS_SINGLE_BATCH_ACTION_NAME, array( $processor_class_name ) );
|
||||||
|
$this->clear_processor_state( $processor_class_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeues and de-schedules all the processors.
|
||||||
|
*/
|
||||||
|
public function force_clear_all_processes(): void {
|
||||||
|
as_unschedule_all_actions( self::PROCESS_SINGLE_BATCH_ACTION_NAME );
|
||||||
|
as_unschedule_all_actions( self::WATCHDOG_ACTION_NAME );
|
||||||
|
|
||||||
|
foreach ( $this->get_enqueued_processors() as $processor ) {
|
||||||
|
$this->clear_processor_state( $processor );
|
||||||
|
}
|
||||||
|
|
||||||
|
update_option( self::ENQUEUED_PROCESSORS_OPTION_NAME, array(), false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an error that happened while processing a batch.
|
||||||
|
*
|
||||||
|
* @param \Exception $error Exception object to log.
|
||||||
|
* @param WCS_Batch_Processor $batch_processor Batch processor instance.
|
||||||
|
* @param array $batch Batch that was being processed.
|
||||||
|
*/
|
||||||
|
protected function log_error( \Exception $error, WCS_Batch_Processor $batch_processor, array $batch ): void {
|
||||||
|
$error_message = "Error processing batch for {$batch_processor->get_name()}: {$error->getMessage()}";
|
||||||
|
$error_context = array(
|
||||||
|
'exception' => $error,
|
||||||
|
'source' => self::LOGS_CONTEXT,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Log only first and last, as the entire batch may be too big.
|
||||||
|
if ( count( $batch ) > 0 ) {
|
||||||
|
$error_context = array_merge(
|
||||||
|
$error_context,
|
||||||
|
array(
|
||||||
|
'batch_start' => $batch[0],
|
||||||
|
'batch_end' => end( $batch ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the error message for a batch processing.
|
||||||
|
*
|
||||||
|
* @param string $error_message The error message that will be logged.
|
||||||
|
* @param \Exception $error The exception that was thrown by the processor.
|
||||||
|
* @param WCS_Batch_Processor $batch_processor The processor that threw the exception.
|
||||||
|
* @param array $batch The batch that was being processed.
|
||||||
|
* @param array $error_context Context to be passed to the logging function.
|
||||||
|
* @return string The actual error message that will be logged.
|
||||||
|
*
|
||||||
|
* @since 7.7.0
|
||||||
|
*/
|
||||||
|
$error_message = apply_filters( 'wcs_batch_processing_log_message', $error_message, $error, $batch_processor, $batch, $error_context );
|
||||||
|
|
||||||
|
$this->logger->error( $error_message, $error_context );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a given processor is consistently failing based on how many recent consecutive failures it has had.
|
||||||
|
*
|
||||||
|
* @param WCS_Batch_Processor $batch_processor The processor that we want to check.
|
||||||
|
* @return boolean TRUE if processor is consistently failing. FALSE otherwise.
|
||||||
|
*/
|
||||||
|
private function is_consistently_failing( WCS_Batch_Processor $batch_processor ): bool {
|
||||||
|
$process_details = $this->get_process_details( $batch_processor );
|
||||||
|
$max_attempts = absint(
|
||||||
|
/**
|
||||||
|
* Controls the failure threshold for batch processors. That is, the number of times we'll attempt to
|
||||||
|
* process a batch that has resulted in a failure. Once above this threshold, the processor won't be
|
||||||
|
* re-scheduled and will be removed from the queue.
|
||||||
|
*
|
||||||
|
* @since 7.7.0
|
||||||
|
*
|
||||||
|
* @param int $failure_threshold Maximum number of times for the processor to try processing a given batch.
|
||||||
|
* @param WCS_Batch_Processor $batch_processor The processor instance.
|
||||||
|
* @param array $process_details Array with batch processor state.
|
||||||
|
*/
|
||||||
|
apply_filters(
|
||||||
|
'wcs_batch_processing_max_attempts',
|
||||||
|
self::FAILING_PROCESS_MAX_ATTEMPTS_DEFAULT,
|
||||||
|
$batch_processor,
|
||||||
|
$process_details
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return absint( $process_details['recent_failures'] ?? 0 ) >= max( $max_attempts, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates log entry with details about a batch processor that is consistently failing.
|
||||||
|
*
|
||||||
|
* @param WCS_Batch_Processor $batch_processor The batch processor instance.
|
||||||
|
* @param array $process_details Failing process details.
|
||||||
|
*/
|
||||||
|
private function log_consistent_failure( WCS_Batch_Processor $batch_processor, array $process_details ): void {
|
||||||
|
$this->logger->error(
|
||||||
|
"Batch processor {$batch_processor->get_name()} appears to be failing consistently: {$process_details['recent_failures']} unsuccessful attempt(s). No further attempts will be made.",
|
||||||
|
array(
|
||||||
|
'source' => self::LOGS_CONTEXT,
|
||||||
|
'failures' => $process_details['recent_failures'],
|
||||||
|
'first_failure' => $process_details['batch_first_failure'],
|
||||||
|
'last_failure' => $process_details['batch_last_failure'],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooked onto 'shutdown'. This cleanup routine checks enqueued processors and whether they are scheduled or not to
|
||||||
|
* either re-eschedule them or remove them from the queue.
|
||||||
|
* This prevents stale states where Action Scheduler won't schedule any more attempts but we still report the
|
||||||
|
* processor as enqueued.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private function remove_or_retry_failed_processors(): void {
|
||||||
|
if ( ! did_action( 'wp_loaded' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$last_error = error_get_last();
|
||||||
|
if ( ! is_null( $last_error ) && in_array( $last_error['type'], array( E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ), true ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The most efficient way to check for an existing action is to use `as_has_scheduled_action`, but in unusual
|
||||||
|
// cases where another plugin has loaded a very old version of Action Scheduler, it may not be available to us.
|
||||||
|
$has_scheduled_action = function_exists( 'as_has_scheduled_action' ) ? 'as_has_scheduled_action' : 'as_next_scheduled_action';
|
||||||
|
|
||||||
|
if ( call_user_func( $has_scheduled_action, self::WATCHDOG_ACTION_NAME ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$enqueued_processors = $this->get_enqueued_processors();
|
||||||
|
$unscheduled_processors = array_diff( $enqueued_processors, array_filter( $enqueued_processors, array( $this, 'is_scheduled' ) ) );
|
||||||
|
|
||||||
|
foreach ( $unscheduled_processors as $processor ) {
|
||||||
|
try {
|
||||||
|
$instance = $this->get_processor_instance( $processor );
|
||||||
|
} catch ( \Exception $e ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exception = new \Exception( 'Processor is enqueued but not scheduled. Background job was probably killed or marked as failed. Reattempting execution.' );
|
||||||
|
$this->update_processor_state( $instance, 0, $exception );
|
||||||
|
$this->log_error( $exception, $instance, array() );
|
||||||
|
|
||||||
|
if ( $this->is_consistently_failing( $instance ) ) {
|
||||||
|
$this->log_consistent_failure( $instance, $this->get_process_details( $instance ) );
|
||||||
|
$this->remove_processor( $processor );
|
||||||
|
} else {
|
||||||
|
$this->schedule_batch_processing( $processor, true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1307,11 +1307,17 @@ class WCS_Cart_Renewal {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$cart_fees = $cart->get_fees();
|
$renewal_order = $this->get_order();
|
||||||
|
|
||||||
|
if ( ! $renewal_order ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Fees are naturally recurring if they have been applied to the renewal order. Generate a key (name + amount) for each fee applied to the order.
|
// Fees are naturally recurring if they have been applied to the renewal order. Generate a key (name + amount) for each fee applied to the order.
|
||||||
$renewal_order_fees = array();
|
$renewal_order_fees = array();
|
||||||
foreach ( $this->get_order()->get_fees() as $item_id => $fee_line_item ) {
|
$cart_fees = $cart->get_fees();
|
||||||
|
|
||||||
|
foreach ( $renewal_order->get_fees() as $item_id => $fee_line_item ) {
|
||||||
$renewal_order_fees[ $item_id ] = $fee_line_item->get_name() . wc_format_decimal( $fee_line_item->get_total() );
|
$renewal_order_fees[ $item_id ] = $fee_line_item->get_name() . wc_format_decimal( $fee_line_item->get_total() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -150,7 +150,8 @@ class WCS_Core_Autoloader {
|
|||||||
*/
|
*/
|
||||||
protected function is_class_interface( $class ) {
|
protected function is_class_interface( $class ) {
|
||||||
static $interfaces = array(
|
static $interfaces = array(
|
||||||
'wcs_cache_updater' => true,
|
'wcs_cache_updater' => true,
|
||||||
|
'wcs_batch_processor' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
return isset( $interfaces[ $class ] );
|
return isset( $interfaces[ $class ] );
|
||||||
@@ -227,7 +228,10 @@ class WCS_Core_Autoloader {
|
|||||||
if ( false !== strpos( $class, 'handler' ) ) {
|
if ( false !== strpos( $class, 'handler' ) ) {
|
||||||
$path .= '/deprecation-handlers';
|
$path .= '/deprecation-handlers';
|
||||||
}
|
}
|
||||||
} elseif ( false !== strpos( $class, 'email' ) && 'wc_subscriptions_email' !== $class ) {
|
} elseif ( false !== strpos( $class, 'email' )
|
||||||
|
&& 'wc_subscriptions_email' !== $class
|
||||||
|
&& 'wc_subscriptions_email_notifications' !== $class
|
||||||
|
) {
|
||||||
$path .= '/emails';
|
$path .= '/emails';
|
||||||
} elseif ( false !== strpos( $class, 'gateway' ) && 'wc_subscriptions_change_payment_gateway' !== $class ) {
|
} elseif ( false !== strpos( $class, 'gateway' ) && 'wc_subscriptions_change_payment_gateway' !== $class ) {
|
||||||
$path .= '/gateways';
|
$path .= '/gateways';
|
||||||
|
274
vendor/woocommerce/subscriptions-core/includes/class-wcs-notifications-batch-processor.php
vendored
Normal file
274
vendor/woocommerce/subscriptions-core/includes/class-wcs-notifications-batch-processor.php
vendored
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce Subscriptions Notifications Batch Processor.
|
||||||
|
*
|
||||||
|
* This batch processor is used to process subscriptions whenever global settings get updated
|
||||||
|
* (global on/off for notifications or time offset).
|
||||||
|
*
|
||||||
|
* It will only process subscriptions whose update time is before the time when the settings got updated.
|
||||||
|
* To ensure all subscription end up having correct notifications, the hook
|
||||||
|
* WCS_Action_Scheduler_Customer_Notifications::update_notifications will update any notifications
|
||||||
|
* whose update time is before the settings got updated. The rest of subscriptions should be updated by this
|
||||||
|
* batch processor.
|
||||||
|
*
|
||||||
|
* In addition to this batch processor which runs ad-hoc, there's also a debug tool to regenerate notifications for
|
||||||
|
* all subscriptions: WCS_Notifications_Debug_Tool_Processor.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Subscriptions
|
||||||
|
* @category Class
|
||||||
|
* @since 7.7.0
|
||||||
|
*/
|
||||||
|
class WCS_Notifications_Batch_Processor implements WCS_Batch_Processor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user-friendly name for this processor.
|
||||||
|
*
|
||||||
|
* @return string Name of the processor.
|
||||||
|
*/
|
||||||
|
public function get_name(): string {
|
||||||
|
return 'wcs_notifications_batch_processor';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user-friendly description for this processor.
|
||||||
|
*
|
||||||
|
* @return string Description of what this processor does.
|
||||||
|
*/
|
||||||
|
public function get_description(): string {
|
||||||
|
return 'WooCommerce Notifications Batch Processor';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the subscription statuses that should be processed.
|
||||||
|
*
|
||||||
|
* @return array Subscription statuses that should be processed.
|
||||||
|
*/
|
||||||
|
protected function get_subscription_statuses() {
|
||||||
|
$allowed_statuses = array(
|
||||||
|
'active',
|
||||||
|
'pending',
|
||||||
|
'on-hold',
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_map( 'wcs_sanitize_subscription_status_key', $allowed_statuses );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the timestamp of the last time the notification settings were updated.
|
||||||
|
*
|
||||||
|
* @return string Datetime of the last time the notification settings were updated.
|
||||||
|
*/
|
||||||
|
public function get_notification_settings_update_time() {
|
||||||
|
$notification_settings_update_timestamp = get_option( 'wcs_notification_settings_update_time', 0 );
|
||||||
|
if ( 0 === $notification_settings_update_timestamp ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$notification_settings_update_time = new DateTime( "@$notification_settings_update_timestamp", new DateTimeZone( 'UTC' ) );
|
||||||
|
return $notification_settings_update_time->format( 'Y-m-d H:i:s' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total number of pending items that require processing.
|
||||||
|
* Once an item is successfully processed by 'process_batch' it shouldn't be included in this count.
|
||||||
|
*
|
||||||
|
* Note that once the processor is enqueued the batch processor controller will keep
|
||||||
|
* invoking `get_next_batch_to_process` and `process_batch` repeatedly until this method returns zero.
|
||||||
|
*
|
||||||
|
* Since this batch processor updates only subscriptions older than the settings update,
|
||||||
|
* it only selects subscriptions updated before the settings update time.
|
||||||
|
*
|
||||||
|
* @return int Number of items pending processing.
|
||||||
|
*/
|
||||||
|
public function get_total_pending_count(): int {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
if ( empty( $this->get_notification_settings_update_time() ) ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowed_statuses = $this->get_subscription_statuses();
|
||||||
|
$placeholders = implode( ', ', array_fill( 0, count( $allowed_statuses ), '%s' ) );
|
||||||
|
|
||||||
|
if ( wcs_is_custom_order_tables_usage_enabled() ) {
|
||||||
|
return $wpdb->get_var(
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT
|
||||||
|
COUNT(*)
|
||||||
|
FROM {$wpdb->prefix}wc_orders
|
||||||
|
WHERE type='shop_subscription'
|
||||||
|
AND date_updated_gmt < %s
|
||||||
|
AND status IN ($placeholders)
|
||||||
|
",
|
||||||
|
$this->get_notification_settings_update_time(),
|
||||||
|
...$allowed_statuses
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return $wpdb->get_var(
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT
|
||||||
|
COUNT(*)
|
||||||
|
FROM {$wpdb->prefix}posts
|
||||||
|
WHERE post_type='shop_subscription'
|
||||||
|
AND post_modified_gmt < %s
|
||||||
|
AND post_status IN ($placeholders)
|
||||||
|
",
|
||||||
|
$this->get_notification_settings_update_time(),
|
||||||
|
...$allowed_statuses
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next batch of items that need to be processed.
|
||||||
|
*
|
||||||
|
* A batch item can be anything needed to identify the actual processing to be done,
|
||||||
|
* but whenever possible items should be numbers (e.g. database record ids)
|
||||||
|
* or at least strings, to ease troubleshooting and logging in case of problems.
|
||||||
|
*
|
||||||
|
* The size of the batch returned can be less than $size if there aren't that
|
||||||
|
* many items pending processing (and it can be zero if there isn't anything to process),
|
||||||
|
* but the size should always be consistent with what 'get_total_pending_count' returns
|
||||||
|
* (i.e. the size of the returned batch shouldn't be larger than the pending items count).
|
||||||
|
*
|
||||||
|
* @param int $size Maximum size of the batch to be returned.
|
||||||
|
*
|
||||||
|
* @return array Batch of items to process, containing $size or less items.
|
||||||
|
*/
|
||||||
|
public function get_next_batch_to_process( int $size ): array {
|
||||||
|
global $wpdb;
|
||||||
|
|
||||||
|
if ( empty( $this->get_notification_settings_update_time() ) ) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowed_statuses = $this->get_subscription_statuses();
|
||||||
|
$placeholders = implode( ', ', array_fill( 0, count( $allowed_statuses ), '%s' ) );
|
||||||
|
|
||||||
|
$args = array_merge(
|
||||||
|
array( $this->get_notification_settings_update_time() ),
|
||||||
|
$allowed_statuses,
|
||||||
|
array( $size ),
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( wcs_is_custom_order_tables_usage_enabled() ) {
|
||||||
|
return $wpdb->get_col(
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT
|
||||||
|
id
|
||||||
|
FROM {$wpdb->prefix}wc_orders
|
||||||
|
WHERE type='shop_subscription'
|
||||||
|
AND date_updated_gmt < %s
|
||||||
|
AND status IN ($placeholders)
|
||||||
|
ORDER BY id ASC
|
||||||
|
LIMIT %d",
|
||||||
|
...$args
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return $wpdb->get_col(
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||||
|
// phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||||
|
$wpdb->prepare(
|
||||||
|
"SELECT
|
||||||
|
ID
|
||||||
|
FROM {$wpdb->prefix}posts
|
||||||
|
WHERE post_type='shop_subscription'
|
||||||
|
AND post_modified_gmt < %s
|
||||||
|
AND post_status IN ($placeholders)
|
||||||
|
ORDER BY ID ASC
|
||||||
|
LIMIT %d",
|
||||||
|
...$args
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process data for the supplied batch: update all notifications for given batch of subscriptions.
|
||||||
|
*
|
||||||
|
* This method should be prepared to receive items that don't actually need processing
|
||||||
|
* (because they have been processed before) and ignore them, but if at least
|
||||||
|
* one of the batch items that actually need processing can't be processed, an exception should be thrown.
|
||||||
|
*
|
||||||
|
* Once an item has been processed it shouldn't be counted in 'get_total_pending_count'
|
||||||
|
* nor included in 'get_next_batch_to_process' anymore (unless something happens that causes it
|
||||||
|
* to actually require further processing).
|
||||||
|
*
|
||||||
|
* @throw \Exception Something went wrong while processing the batch.
|
||||||
|
*
|
||||||
|
* @param array $batch Batch to process, as returned by 'get_next_batch_to_process'.
|
||||||
|
*/
|
||||||
|
public function process_batch( array $batch ): void {
|
||||||
|
// Instantiating this again would hook another set of hooks for update_status and update_date. No bueno.
|
||||||
|
$subscriptions_notifications = WC_Subscriptions_Core_Plugin::instance()->notifications_scheduler;
|
||||||
|
|
||||||
|
foreach ( $batch as $subscription_id ) {
|
||||||
|
$subscription = wcs_get_subscription( $subscription_id );
|
||||||
|
|
||||||
|
if ( ! $subscription ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( WC_Subscriptions_Email_Notifications::notifications_globally_enabled() ) {
|
||||||
|
$subscriptions_notifications->update_status( $subscription, $subscription->get_status(), null );
|
||||||
|
} else {
|
||||||
|
$subscriptions_notifications->unschedule_all_notifications( $subscription );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the subscription's update time to mark it as updated.
|
||||||
|
$subscription->set_date_modified( time() );
|
||||||
|
$subscription->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default (preferred) batch size to pass to 'get_next_batch_to_process'.
|
||||||
|
* The controller will pass this size unless it's externally configured
|
||||||
|
* to use a different size.
|
||||||
|
*
|
||||||
|
* @return int Default batch size.
|
||||||
|
*/
|
||||||
|
public function get_default_batch_size(): int {
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the background process for updating notifications.
|
||||||
|
*
|
||||||
|
* @return string Informative string to show after the tool is triggered in UI.
|
||||||
|
*/
|
||||||
|
public static function enqueue(): string {
|
||||||
|
$batch_processor = WCS_Batch_Processing_Controller::instance();
|
||||||
|
if ( $batch_processor->is_enqueued( self::class ) ) {
|
||||||
|
return __( 'Background process for updating subscription notifications already started, nothing done.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$batch_processor->enqueue_processor( self::class );
|
||||||
|
return __( 'Background process for updating subscription notifications started.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the background process for updating notifications.
|
||||||
|
*
|
||||||
|
* @return string Informative string to show after the tool is triggered in UI.
|
||||||
|
*/
|
||||||
|
public static function dequeue(): string {
|
||||||
|
$batch_processor = WCS_Batch_Processing_Controller::instance();
|
||||||
|
if ( ! $batch_processor->is_enqueued( self::class ) ) {
|
||||||
|
return __( 'Background process for updating subscription notifications not started, nothing done.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$batch_processor->remove_processor( self::class );
|
||||||
|
return __( 'Background process for updating subscription notifications stopped.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
}
|
@@ -46,7 +46,7 @@ class WCS_User_Change_Status_Handler {
|
|||||||
*/
|
*/
|
||||||
public static function change_users_subscription( $subscription, $new_status ) {
|
public static function change_users_subscription( $subscription, $new_status ) {
|
||||||
$subscription = ( ! is_object( $subscription ) ) ? wcs_get_subscription( $subscription ) : $subscription;
|
$subscription = ( ! is_object( $subscription ) ) ? wcs_get_subscription( $subscription ) : $subscription;
|
||||||
$changed = false;
|
$changed = false;
|
||||||
|
|
||||||
do_action( 'woocommerce_before_customer_changed_subscription_to_' . $new_status, $subscription );
|
do_action( 'woocommerce_before_customer_changed_subscription_to_' . $new_status, $subscription );
|
||||||
|
|
||||||
|
@@ -171,7 +171,7 @@ class WCS_Subscription_Data_Store_CPT extends WC_Order_Data_Store_CPT implements
|
|||||||
|
|
||||||
foreach ( $this->subscription_meta_keys_to_props as $meta_key => $prop_key ) {
|
foreach ( $this->subscription_meta_keys_to_props as $meta_key => $prop_key ) {
|
||||||
if ( 0 === strpos( $prop_key, 'schedule' ) || in_array( $meta_key, $this->subscription_internal_meta_keys ) ) {
|
if ( 0 === strpos( $prop_key, 'schedule' ) || in_array( $meta_key, $this->subscription_internal_meta_keys ) ) {
|
||||||
|
// Keeping this occurrence of `get_post_meta()` as get_post here does not work well.
|
||||||
$meta_value = get_post_meta( $subscription->get_id(), $meta_key, true );
|
$meta_value = get_post_meta( $subscription->get_id(), $meta_key, true );
|
||||||
|
|
||||||
// Dates are set via update_dates() to make sure relationships between dates are validated
|
// Dates are set via update_dates() to make sure relationships between dates are validated
|
||||||
@@ -201,7 +201,7 @@ class WCS_Subscription_Data_Store_CPT extends WC_Order_Data_Store_CPT implements
|
|||||||
* @see https://github.com/Prospress/woocommerce-subscriptions/issues/3036
|
* @see https://github.com/Prospress/woocommerce-subscriptions/issues/3036
|
||||||
*/
|
*/
|
||||||
if ( '3.5.0' === WC()->version ) {
|
if ( '3.5.0' === WC()->version ) {
|
||||||
$props_to_set['customer_id'] = get_post_meta( $subscription->get_id(), '_customer_user', true );
|
$props_to_set['customer_id'] = $subscription->get_meta( '_customer_user', true );
|
||||||
}
|
}
|
||||||
|
|
||||||
$subscription->update_dates( $dates_to_set );
|
$subscription->update_dates( $dates_to_set );
|
||||||
|
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer Notification: Automated Subscription Renewal.
|
||||||
|
*
|
||||||
|
* An email sent to the customer when a subscription will be renewed automatically.
|
||||||
|
*
|
||||||
|
* @class WCS_Email_Customer_Notification_Auto_Renewal
|
||||||
|
* @version 1.0.0
|
||||||
|
* @package WooCommerce_Subscriptions/Classes/Emails
|
||||||
|
* @extends WC_Email
|
||||||
|
*/
|
||||||
|
class WCS_Email_Customer_Notification_Auto_Renewal extends WCS_Email_Customer_Notification {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the class.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
$this->plugin_id = 'woocommerce-subscriptions_';
|
||||||
|
|
||||||
|
$this->id = 'customer_notification_auto_renewal';
|
||||||
|
$this->title = __( 'Customer Notification: Automatic renewal notice', 'woocommerce-subscriptions' );
|
||||||
|
$this->description = __( 'Customer Notification: Automatic renewal notice emails are sent when customer\'s subscription is about to be renewed automatically.', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
$this->heading = __( 'Automatic renewal notice', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
$this->subject = sprintf(
|
||||||
|
// translators: $1: {site_title}, $2: {customers_first_name}, $3: {time_until_renewal}, variables that will be substituted when email is sent out
|
||||||
|
_x( '[%1$s] %2$s, your subscription automatically renews in %3$s!', 'default email subject for subscription\'s automatic renewal notice', 'woocommerce-subscriptions' ),
|
||||||
|
'{site_title}',
|
||||||
|
'{customers_first_name}',
|
||||||
|
'{time_until_renewal}',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->template_html = 'emails/customer-notification-auto-renewal.php';
|
||||||
|
$this->template_plain = 'emails/plain/customer-notification-auto-renewal.php';
|
||||||
|
$this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' );
|
||||||
|
|
||||||
|
$this->customer_email = true;
|
||||||
|
|
||||||
|
// Constructor in parent uses the values above in the initialization.
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_relevant_date_type() {
|
||||||
|
return 'next_payment';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default content to show below main email content.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_default_additional_content() {
|
||||||
|
return __( 'Thank you for being a loyal customer, {customers_first_name} — we appreciate your business.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer Notification: Free Trial Expiring Subscription Email
|
||||||
|
*
|
||||||
|
* An email sent to the customer when a free trial is about to end.
|
||||||
|
*
|
||||||
|
* @class WCS_Email_Customer_Notification_Free_Trial_Expiry
|
||||||
|
* @version 1.0.0
|
||||||
|
* @package WooCommerce_Subscriptions/Classes/Emails
|
||||||
|
* @extends WC_Email
|
||||||
|
*/
|
||||||
|
class WCS_Email_Customer_Notification_Auto_Trial_Expiration extends WCS_Email_Customer_Notification {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the class.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
$this->plugin_id = 'woocommerce-subscriptions_';
|
||||||
|
|
||||||
|
$this->id = 'customer_notification_auto_trial_expiry';
|
||||||
|
$this->title = __( 'Customer Notification: Free trial expiration: automatic payment notice', 'woocommerce-subscriptions' );
|
||||||
|
$this->description = __( 'Free trial expiry notification emails are sent when customer\'s free trial for an automatically renewd subscription is about to expire.', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
$this->heading = __( 'Free trial expiration: automatic payment notice', 'woocommerce-subscriptions' );
|
||||||
|
// translators: $1: {site_title}, $2: {customers_first_name}, variables that will be substituted when email is sent out
|
||||||
|
$this->subject = sprintf( _x( '[%1$s] %2$s, your paid subscription starts soon!', 'default email subject for free trial expiry notification emails sent to the customer', 'woocommerce-subscriptions' ), '{site_title}', '{customers_first_name}' );
|
||||||
|
|
||||||
|
$this->template_html = 'emails/customer-notification-auto-trial-ending.php';
|
||||||
|
$this->template_plain = 'emails/plain/customer-notification-auto-trial-ending.php';
|
||||||
|
$this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' );
|
||||||
|
|
||||||
|
$this->customer_email = true;
|
||||||
|
|
||||||
|
// Constructor in parent uses the values above in the initialization.
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_relevant_date_type() {
|
||||||
|
return 'trial_end';
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer Notification: Manual Subscription Renewal.
|
||||||
|
*
|
||||||
|
* An email sent to the customer when a subscription needs to be renewed manually.
|
||||||
|
*
|
||||||
|
* @class WCS_Email_Customer_Notification_Manual_Renewal
|
||||||
|
* @version 1.0.0
|
||||||
|
* @package WooCommerce_Subscriptions/Classes/Emails
|
||||||
|
* @extends WC_Email
|
||||||
|
*/
|
||||||
|
class WCS_Email_Customer_Notification_Manual_Renewal extends WCS_Email_Customer_Notification {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the class.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
$this->plugin_id = 'woocommerce-subscriptions_';
|
||||||
|
|
||||||
|
$this->id = 'customer_notification_manual_renewal';
|
||||||
|
$this->title = __( 'Customer Notification: Manual renewal notice', 'woocommerce-subscriptions' );
|
||||||
|
$this->description = __( 'Customer Notification: Manual renewal notice are sent when customer\'s subscription needs to be manually renewed.', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
$this->heading = __( 'Manual renewal notice', 'woocommerce-subscriptions' );
|
||||||
|
// translators: $1: {site_title}, $2: {customers_first_name}, variables that will be substituted when email is sent out
|
||||||
|
$this->subject = sprintf( _x( '[%1$s] %2$s, your subscription is ready to be renewed!', 'default email subject for notification for a manually renewed subscription sent to the customer', 'woocommerce-subscriptions' ), '{site_title}', '{customers_first_name}' );
|
||||||
|
|
||||||
|
$this->template_html = 'emails/customer-notification-manual-renewal.php';
|
||||||
|
$this->template_plain = 'emails/plain/customer-notification-manual-renewal.php';
|
||||||
|
$this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' );
|
||||||
|
|
||||||
|
$this->customer_email = true;
|
||||||
|
|
||||||
|
// Constructor in parent uses the values above in the initialization.
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_relevant_date_type() {
|
||||||
|
return 'next_payment';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default content to show below main email content.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_default_additional_content() {
|
||||||
|
return __( 'Thanks again for choosing {site_title}.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer Notification: Free Trial Expiring Subscription Email
|
||||||
|
*
|
||||||
|
* An email sent to the customer when a free trial is about to end.
|
||||||
|
*
|
||||||
|
* @class WCS_Email_Customer_Notification_Free_Trial_Expiry
|
||||||
|
* @version 1.0.0
|
||||||
|
* @package WooCommerce_Subscriptions/Classes/Emails
|
||||||
|
* @extends WC_Email
|
||||||
|
*/
|
||||||
|
class WCS_Email_Customer_Notification_Manual_Trial_Expiration extends WCS_Email_Customer_Notification {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the class.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
|
||||||
|
$this->plugin_id = 'woocommerce-subscriptions_';
|
||||||
|
|
||||||
|
$this->id = 'customer_notification_manual_trial_expiry';
|
||||||
|
$this->title = __( 'Customer Notification: Free trial expiration: manual payment required', 'woocommerce-subscriptions' );
|
||||||
|
$this->description = __( 'Free trial expiry notification emails are sent when customer\'s free trial for a manually renewed subscription is about to expire.', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
$this->heading = __( 'Free trial expiration: manual payment required', 'woocommerce-subscriptions' );
|
||||||
|
// translators: $1: {site_title}, $2: {customers_first_name}, variables that will be substituted when email is sent out.
|
||||||
|
$this->subject = sprintf( _x( '[%1$s] %2$s, your free trial is almost up!', 'default email subject for an email notification for a manually renewed subscription with free trial expiry emails sent to the customer', 'woocommerce-subscriptions' ), '{site_title}', '{customers_first_name}' );
|
||||||
|
|
||||||
|
$this->template_html = 'emails/customer-notification-manual-trial-ending.php';
|
||||||
|
$this->template_plain = 'emails/plain/customer-notification-manual-trial-ending.php';
|
||||||
|
$this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' );
|
||||||
|
|
||||||
|
$this->customer_email = true;
|
||||||
|
|
||||||
|
// Constructor in parent uses the values above in the initialization.
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_relevant_date_type() {
|
||||||
|
return 'trial_end';
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer Notification: Subscription Expiring email
|
||||||
|
*
|
||||||
|
* An email sent to the customer when a subscription is about to expire.
|
||||||
|
*
|
||||||
|
* @class WCS_Email_Customer_Notification_Subscription_Expiring
|
||||||
|
* @version 1.0.0
|
||||||
|
* @package WooCommerce_Subscriptions/Classes/Emails
|
||||||
|
* @extends WC_Email
|
||||||
|
*/
|
||||||
|
class WCS_Email_Customer_Notification_Subscription_Expiration extends WCS_Email_Customer_Notification {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an instance of the class.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
$this->plugin_id = 'woocommerce-subscriptions_';
|
||||||
|
|
||||||
|
$this->id = 'customer_notification_subscription_expiry';
|
||||||
|
$this->title = __( 'Customer Notification: Subscription expiration notice', 'woocommerce-subscriptions' );
|
||||||
|
$this->description = __( 'Subscription expiration notification emails are sent when customer\'s subscription is about to expire.', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
$this->heading = __( 'Subscription expiration notice', 'woocommerce-subscriptions' );
|
||||||
|
// translators: $1: {site_title}, $2: {customers_first_name}, variables that will be substituted when email is sent out
|
||||||
|
$this->subject = sprintf( _x( '[%1$s] %2$s, your subscription is about to expire!', 'default email subject for subscription expiry notification email sent to the customer', 'woocommerce-subscriptions' ), '{site_title}', '{customers_first_name}' );
|
||||||
|
|
||||||
|
$this->template_html = 'emails/customer-notification-expiring-subscription.php';
|
||||||
|
$this->template_plain = 'emails/plain/customer-notification-expiring-subscription.php';
|
||||||
|
$this->template_base = WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory( 'templates/' );
|
||||||
|
|
||||||
|
$this->customer_email = true;
|
||||||
|
|
||||||
|
// Constructor in parent uses the values above in the initialization.
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_relevant_date_type() {
|
||||||
|
return 'end';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default content to show below main email content.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_default_additional_content() {
|
||||||
|
return __( 'Thank you for choosing {site_title}, {customers_first_name}.', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
}
|
296
vendor/woocommerce/subscriptions-core/includes/emails/class-wcs-email-customer-notification.php
vendored
Normal file
296
vendor/woocommerce/subscriptions-core/includes/emails/class-wcs-email-customer-notification.php
vendored
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer notification email
|
||||||
|
*
|
||||||
|
* Customer notification email sent to customer when a there's an upcoming payment/expity/free trial expiry.
|
||||||
|
*
|
||||||
|
* @class WCS_Email_Customer_Notification
|
||||||
|
* @version 7.7.0
|
||||||
|
* @package WooCommerce/Classes/Emails
|
||||||
|
*/
|
||||||
|
class WCS_Email_Customer_Notification extends WC_Email {
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
// These values are only available later, but it's an available placeholder.
|
||||||
|
$this->placeholders = array_merge(
|
||||||
|
[
|
||||||
|
'{customers_first_name}' => '',
|
||||||
|
'{time_until_renewal}' => '',
|
||||||
|
],
|
||||||
|
$this->placeholders
|
||||||
|
);
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise Settings Form Fields - these are generic email options most will use.
|
||||||
|
*/
|
||||||
|
public function init_form_fields() {
|
||||||
|
/* translators: %s: list of placeholders */
|
||||||
|
$placeholder_text = sprintf( __( 'Available placeholders: %s', 'woocommerce-subscriptions' ), '<code>' . esc_html( implode( '</code>, <code>', array_keys( $this->placeholders ) ) ) . '</code>' );
|
||||||
|
$this->form_fields = array(
|
||||||
|
'enabled' => array(
|
||||||
|
'title' => __( 'Enable/Disable', 'woocommerce-subscriptions' ),
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'label' => __( 'Enable this email notification. Disabled automatically on staging sites.', 'woocommerce-subscriptions' ),
|
||||||
|
'default' => 'yes',
|
||||||
|
),
|
||||||
|
'subject' => array(
|
||||||
|
'title' => __( 'Subject', 'woocommerce-subscriptions' ),
|
||||||
|
'type' => 'text',
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => $placeholder_text,
|
||||||
|
'placeholder' => $this->get_default_subject(),
|
||||||
|
'default' => '',
|
||||||
|
),
|
||||||
|
'heading' => array(
|
||||||
|
'title' => __( 'Email heading', 'woocommerce-subscriptions' ),
|
||||||
|
'type' => 'text',
|
||||||
|
'desc_tip' => true,
|
||||||
|
'description' => $placeholder_text,
|
||||||
|
'placeholder' => $this->get_default_heading(),
|
||||||
|
'default' => '',
|
||||||
|
),
|
||||||
|
'additional_content' => array(
|
||||||
|
'title' => __( 'Additional content', 'woocommerce-subscriptions' ),
|
||||||
|
'description' => __( 'Text to appear below the main email content.', 'woocommerce-subscriptions' ) . ' ' . $placeholder_text,
|
||||||
|
'css' => 'width:400px; height: 75px;',
|
||||||
|
'placeholder' => __( 'N/A', 'woocommerce-subscriptions' ),
|
||||||
|
'type' => 'textarea',
|
||||||
|
'default' => $this->get_default_additional_content(),
|
||||||
|
'desc_tip' => true,
|
||||||
|
),
|
||||||
|
'email_type' => array(
|
||||||
|
'title' => __( 'Email type', 'woocommerce-subscriptions' ),
|
||||||
|
'type' => 'select',
|
||||||
|
'description' => __( 'Choose which format of email to send.', 'woocommerce-subscriptions' ),
|
||||||
|
'default' => 'html',
|
||||||
|
'class' => 'email_type wc-enhanced-select',
|
||||||
|
'options' => $this->get_email_type_options(),
|
||||||
|
'desc_tip' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger function.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function trigger( $subscription_id ) {
|
||||||
|
$subscription = wcs_get_subscription( $subscription_id );
|
||||||
|
$this->object = $subscription;
|
||||||
|
$this->recipient = $subscription->get_billing_email();
|
||||||
|
|
||||||
|
if ( ! $this->should_send_reminder_email( $subscription ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setup_locale();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->placeholders['{customers_first_name}'] = $subscription->get_billing_first_name();
|
||||||
|
$this->placeholders['{time_until_renewal}'] = $this->get_time_until_date( $subscription, 'next_payment' );
|
||||||
|
|
||||||
|
$result = $this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
|
||||||
|
|
||||||
|
if ( $result ) {
|
||||||
|
/* translators: 1: Notification type, 2: customer's email. */
|
||||||
|
$order_note_msg = sprintf( __( '%1$s was successfully sent to %2$s.', 'woocommerce-subscriptions' ), $this->title, $this->recipient );
|
||||||
|
} else {
|
||||||
|
/* translators: 1: Notification type, 2: customer's email. */
|
||||||
|
$order_note_msg = sprintf( __( 'Attempt to send %1$s to %2$s failed.', 'woocommerce-subscriptions' ), $this->title, $this->recipient );
|
||||||
|
}
|
||||||
|
|
||||||
|
$subscription->add_order_note( $order_note_msg );
|
||||||
|
} finally {
|
||||||
|
$this->restore_locale();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get content for the HTML-version of the email.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_content_html() {
|
||||||
|
$subscription = $this->object;
|
||||||
|
|
||||||
|
if ( wcs_can_user_renew_early( $subscription )
|
||||||
|
&& $subscription->payment_method_supports( 'subscription_date_changes' )
|
||||||
|
&& WCS_Early_Renewal_Manager::is_early_renewal_enabled()
|
||||||
|
&& WCS_Manual_Renewal_Manager::is_manual_renewal_enabled()
|
||||||
|
) {
|
||||||
|
$url_for_renewal = wcs_get_early_renewal_url( $subscription );
|
||||||
|
$can_renew_early = true;
|
||||||
|
} else {
|
||||||
|
$url_for_renewal = $subscription->get_view_order_url();
|
||||||
|
$can_renew_early = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wc_get_template_html(
|
||||||
|
$this->template_html,
|
||||||
|
[
|
||||||
|
'subscription' => $subscription,
|
||||||
|
'order' => $subscription->get_parent(),
|
||||||
|
'email_heading' => $this->get_heading(),
|
||||||
|
'subscription_time_til_event' => $this->get_time_until_date( $subscription, $this->get_relevant_date_type() ),
|
||||||
|
'subscription_event_date' => $this->get_formatted_date( $subscription, $this->get_relevant_date_type() ),
|
||||||
|
'url_for_renewal' => $url_for_renewal,
|
||||||
|
'can_renew_early' => $can_renew_early,
|
||||||
|
'additional_content' => is_callable(
|
||||||
|
[
|
||||||
|
$this,
|
||||||
|
'get_additional_content',
|
||||||
|
]
|
||||||
|
) ? $this->get_additional_content() : '',
|
||||||
|
// WC 3.7 introduced an additional content field for all emails.
|
||||||
|
'sent_to_admin' => false,
|
||||||
|
'plain_text' => false,
|
||||||
|
'email' => $this,
|
||||||
|
],
|
||||||
|
'',
|
||||||
|
$this->template_base
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get content for the plain (text, non-HTML) version of the email.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_content_plain() {
|
||||||
|
$subscription = $this->object;
|
||||||
|
|
||||||
|
if ( wcs_can_user_renew_early( $subscription )
|
||||||
|
&& $subscription->payment_method_supports( 'subscription_date_changes' )
|
||||||
|
&& WCS_Early_Renewal_Manager::is_early_renewal_enabled()
|
||||||
|
&& WCS_Manual_Renewal_Manager::is_manual_renewal_enabled()
|
||||||
|
) {
|
||||||
|
$url_for_renewal = wcs_get_early_renewal_url( $subscription );
|
||||||
|
$can_renew_early = true;
|
||||||
|
} else {
|
||||||
|
$url_for_renewal = $subscription->get_view_order_url();
|
||||||
|
$can_renew_early = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wc_get_template_html(
|
||||||
|
$this->template_plain,
|
||||||
|
[
|
||||||
|
'subscription' => $subscription,
|
||||||
|
'order' => $subscription->get_parent(),
|
||||||
|
'email_heading' => $this->get_heading(),
|
||||||
|
'subscription_time_til_event' => $this->get_time_until_date( $subscription, $this->get_relevant_date_type() ),
|
||||||
|
'subscription_event_date' => $this->get_formatted_date( $subscription, $this->get_relevant_date_type() ),
|
||||||
|
'url_for_renewal' => $url_for_renewal,
|
||||||
|
'can_renew_early' => $can_renew_early,
|
||||||
|
'additional_content' => is_callable(
|
||||||
|
[
|
||||||
|
$this,
|
||||||
|
'get_additional_content',
|
||||||
|
]
|
||||||
|
) ? $this->get_additional_content() : '',
|
||||||
|
// WC 3.7 introduced an additional content field for all emails.
|
||||||
|
'sent_to_admin' => false,
|
||||||
|
'plain_text' => true,
|
||||||
|
'email' => $this,
|
||||||
|
],
|
||||||
|
'',
|
||||||
|
$this->template_base
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns number of days until date_type for subscription.
|
||||||
|
*
|
||||||
|
* This method is needed when sending out the emails as the email queue might be delayed, in which case the email
|
||||||
|
* should state the correct number of days until the date_type.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription Subscription to check.
|
||||||
|
* @param string $date_type Date type to count days to.
|
||||||
|
*
|
||||||
|
* @return false|int|string Number of days from now until the date type event's time. Empty string if subscription doesn't have the date_type defined. False if DateTime can't process the data.
|
||||||
|
*/
|
||||||
|
public function get_time_until_date( $subscription, $date_type ) {
|
||||||
|
$next_event = $subscription->get_date( $date_type );
|
||||||
|
|
||||||
|
if ( ! $next_event ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$next_event_dt = new DateTime( $next_event, new DateTimeZone( 'UTC' ) );
|
||||||
|
$now = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
|
||||||
|
|
||||||
|
// Both dates to midnight so we only compare days, not hours.
|
||||||
|
$next_event_dt->setTime( 0, 0 );
|
||||||
|
$now->setTime( 0, 0 );
|
||||||
|
|
||||||
|
// Add some buffer, otherwise it will claim that only 2 full days are left when in reality it's 2 days, 23 hours and 59 minutes.
|
||||||
|
$now->modify( '-1 hour' );
|
||||||
|
return human_time_diff( $now->getTimestamp(), $next_event_dt->getTimestamp() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return subscription's date of date type in localized format.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
* @param string $date_type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_formatted_date( $subscription, $date_type ) {
|
||||||
|
return date_i18n( wc_date_format(), $subscription->get_time( $date_type, 'site' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default content to show below main email content.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function get_default_additional_content() {
|
||||||
|
return __( 'Thank you for choosing {site_title}!', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the customer reminder email should be sent and add an order note if it shouldn't.
|
||||||
|
*
|
||||||
|
* Reminder emails are not sent if:
|
||||||
|
* - The Customer Notification feature is disabled.
|
||||||
|
* - The store is a staging or development site.
|
||||||
|
* - The recipient email address is missing.
|
||||||
|
* - The subscription's billing cycle is too short.
|
||||||
|
*
|
||||||
|
* @param WC_Subscription $subscription
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function should_send_reminder_email( $subscription ) {
|
||||||
|
$should_skip = [];
|
||||||
|
|
||||||
|
if ( ! $this->is_enabled() ) {
|
||||||
|
$should_skip[] = __( 'Reminder emails disabled.', 'woocommerce-subscriptions' );
|
||||||
|
} else {
|
||||||
|
if ( ! WC_Subscriptions_Email_Notifications::should_send_notification() ) {
|
||||||
|
$should_skip[] = __( 'Not a production site', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $this->get_recipient() ) {
|
||||||
|
$should_skip[] = __( 'Recipient not found', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( WCS_Action_Scheduler_Customer_Notifications::is_subscription_period_too_short( $subscription ) ) {
|
||||||
|
$should_skip[] = __( 'Subscription billing cycle too short', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $should_skip ) ) {
|
||||||
|
// translators: %1$s: email title, %2$s: list of reasons why email was skipped.
|
||||||
|
$subscription->add_order_note( sprintf( __( 'Skipped sending "%1$s": %2$s', 'woocommerce-subscriptions' ), $this->title, '<br>- ' . implode( '<br>- ', $should_skip ) ) );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -43,7 +43,7 @@ class WCS_PayPal_Change_Payment_Method_Admin {
|
|||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
*/
|
*/
|
||||||
public static function add_payment_meta_details( $payment_meta, $subscription ) {
|
public static function add_payment_meta_details( $payment_meta, $subscription ) {
|
||||||
$subscription_id = get_post_meta( $subscription->get_id(), '_paypal_subscription_id', true );
|
$subscription_id = $subscription->get_meta( '_paypal_subscription_id', true );
|
||||||
|
|
||||||
if ( wcs_is_paypal_profile_a( $subscription_id, 'billing_agreement' ) || empty( $subscription_id ) ) {
|
if ( wcs_is_paypal_profile_a( $subscription_id, 'billing_agreement' ) || empty( $subscription_id ) ) {
|
||||||
$label = 'PayPal Billing Agreement ID';
|
$label = 'PayPal Billing Agreement ID';
|
||||||
|
@@ -139,7 +139,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
|
|||||||
if ( isset( $transaction_details['txn_id'] ) ) {
|
if ( isset( $transaction_details['txn_id'] ) ) {
|
||||||
|
|
||||||
// Make sure the IPN request has not already been handled
|
// Make sure the IPN request has not already been handled
|
||||||
$handled_transactions = get_post_meta( $subscription->get_id(), '_paypal_ipn_tracking_ids', true );
|
$handled_transactions = $subscription->get_meta( '_paypal_ipn_tracking_ids', true );
|
||||||
|
|
||||||
if ( empty( $handled_transactions ) ) {
|
if ( empty( $handled_transactions ) ) {
|
||||||
$handled_transactions = array();
|
$handled_transactions = array();
|
||||||
@@ -190,13 +190,13 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
|
|||||||
$transaction_order = wc_get_order( substr( $transaction_details['invoice'], strrpos( $transaction_details['invoice'], '-' ) + 1 ) );
|
$transaction_order = wc_get_order( substr( $transaction_details['invoice'], strrpos( $transaction_details['invoice'], '-' ) + 1 ) );
|
||||||
|
|
||||||
// check if the failed signup has been previously recorded
|
// check if the failed signup has been previously recorded
|
||||||
if ( wcs_get_objects_property( $transaction_order, 'id' ) != get_post_meta( $subscription->get_id(), '_paypal_failed_sign_up_recorded', true ) ) {
|
if ( wcs_get_objects_property( $transaction_order, 'id' ) !== $subscription->get_meta( '_paypal_failed_sign_up_recorded', true ) ) {
|
||||||
$is_renewal_sign_up_after_failure = true;
|
$is_renewal_sign_up_after_failure = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the invoice ID doesn't match the default invoice ID and contains the string '-wcscpm-', the IPN is for a subscription payment method change
|
// If the invoice ID doesn't match the default invoice ID and contains the string '-wcscpm-', the IPN is for a subscription payment method change
|
||||||
if ( 'subscr_signup' == $transaction_details['txn_type'] && false !== strpos( $transaction_details['invoice'], '-wcscpm-' ) ) {
|
if ( 'subscr_signup' === $transaction_details['txn_type'] && false !== strpos( $transaction_details['invoice'], '-wcscpm-' ) ) {
|
||||||
$is_payment_change = true;
|
$is_payment_change = true;
|
||||||
} else {
|
} else {
|
||||||
$is_payment_change = false;
|
$is_payment_change = false;
|
||||||
@@ -268,8 +268,8 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
|
|||||||
WC_Subscriptions_Change_Payment_Gateway::update_payment_method( $subscription, 'paypal' );
|
WC_Subscriptions_Change_Payment_Gateway::update_payment_method( $subscription, 'paypal' );
|
||||||
|
|
||||||
// We need to cancel the subscription now that the method has been changed successfully
|
// We need to cancel the subscription now that the method has been changed successfully
|
||||||
if ( 'paypal' == get_post_meta( $subscription->get_id(), '_old_payment_method', true ) ) {
|
if ( 'paypal' === $subscription->get_meta( '_old_payment_method', true ) ) {
|
||||||
self::cancel_subscription( $subscription, get_post_meta( $subscription->get_id(), '_old_paypal_subscriber_id', true ) );
|
self::cancel_subscription( $subscription, $subscription->get_meta( '_old_paypal_subscriber_id', true ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->add_order_note( _x( 'IPN subscription payment method changed to PayPal.', 'when it is a payment change, and there is a subscr_signup message, this will be a confirmation message that PayPal accepted it being the new payment method', 'woocommerce-subscriptions' ), $subscription, $transaction_details );
|
$this->add_order_note( _x( 'IPN subscription payment method changed to PayPal.', 'when it is a payment change, and there is a subscr_signup message, this will be a confirmation message that PayPal accepted it being the new payment method', 'woocommerce-subscriptions' ), $subscription, $transaction_details );
|
||||||
@@ -353,7 +353,7 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
|
|||||||
update_post_meta( $subscription->get_id(), '_paypal_first_ipn_ignored_for_pdt', 'true' );
|
update_post_meta( $subscription->get_id(), '_paypal_first_ipn_ignored_for_pdt', 'true' );
|
||||||
|
|
||||||
// Ignore the first IPN message if the PDT should have handled it (if it didn't handle it, it will have been dealt with as first payment), but set a flag to make sure we only ignore it once
|
// Ignore the first IPN message if the PDT should have handled it (if it didn't handle it, it will have been dealt with as first payment), but set a flag to make sure we only ignore it once
|
||||||
} elseif ( $subscription->get_payment_count() == 1 && '' !== WCS_PayPal::get_option( 'identity_token' ) && 'true' != get_post_meta( $subscription->get_id(), '_paypal_first_ipn_ignored_for_pdt', true ) && false === $is_renewal_sign_up_after_failure ) {
|
} elseif ( $subscription->get_payment_count() === 1 && '' !== WCS_PayPal::get_option( 'identity_token' ) && 'true' !== $subscription->get_meta( '_paypal_first_ipn_ignored_for_pdt', true ) && false === $is_renewal_sign_up_after_failure ) {
|
||||||
|
|
||||||
WC_Gateway_Paypal::log( 'IPN subscription payment ignored for subscription ' . $subscription->get_id() . ' due to PDT previously handling the payment.' );
|
WC_Gateway_Paypal::log( 'IPN subscription payment ignored for subscription ' . $subscription->get_id() . ' due to PDT previously handling the payment.' );
|
||||||
|
|
||||||
@@ -367,9 +367,8 @@ class WCS_PayPal_Standard_IPN_Handler extends WC_Gateway_Paypal_IPN_Handler {
|
|||||||
update_post_meta( $subscription->get_id(), '_paypal_failed_sign_up_recorded', wcs_get_objects_property( $transaction_order, 'id' ) );
|
update_post_meta( $subscription->get_id(), '_paypal_failed_sign_up_recorded', wcs_get_objects_property( $transaction_order, 'id' ) );
|
||||||
|
|
||||||
// We need to cancel the old subscription now that the method has been changed successfully
|
// We need to cancel the old subscription now that the method has been changed successfully
|
||||||
if ( 'paypal' == get_post_meta( $subscription->get_id(), '_old_payment_method', true ) ) {
|
if ( 'paypal' === $subscription->get_meta( '_old_payment_method', true ) ) {
|
||||||
|
$profile_id = $subscription->get_meta( '_old_paypal_subscriber_id' );
|
||||||
$profile_id = get_post_meta( $subscription->get_id(), '_old_paypal_subscriber_id', true );
|
|
||||||
|
|
||||||
// Make sure we don't cancel the current profile
|
// Make sure we don't cancel the current profile
|
||||||
if ( $profile_id !== $transaction_details['subscr_id'] ) {
|
if ( $profile_id !== $transaction_details['subscr_id'] ) {
|
||||||
|
81
vendor/woocommerce/subscriptions-core/includes/interfaces/interface-wcs-batch-processor.php
vendored
Normal file
81
vendor/woocommerce/subscriptions-core/includes/interfaces/interface-wcs-batch-processor.php
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* WCS_Batch_Processor Interface
|
||||||
|
*
|
||||||
|
* Interface for batch data processors. See the WCS_Batch_Processing_Controller class for usage details.
|
||||||
|
*
|
||||||
|
* @package WooCommerce Subscriptions
|
||||||
|
* @version 7.7.0
|
||||||
|
* @since 7.7.0
|
||||||
|
*/
|
||||||
|
interface WCS_Batch_Processor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user-friendly name for this processor.
|
||||||
|
*
|
||||||
|
* @return string Name of the processor.
|
||||||
|
*/
|
||||||
|
public function get_name() : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user-friendly description for this processor.
|
||||||
|
*
|
||||||
|
* @return string Description of what this processor does.
|
||||||
|
*/
|
||||||
|
public function get_description() : string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total number of pending items that require processing.
|
||||||
|
* Once an item is successfully processed by 'process_batch' it shouldn't be included in this count.
|
||||||
|
*
|
||||||
|
* Note that the once the processor is enqueued the batch processor controller will keep
|
||||||
|
* invoking `get_next_batch_to_process` and `process_batch` repeatedly until this method returns zero.
|
||||||
|
*
|
||||||
|
* @return int Number of items pending processing.
|
||||||
|
*/
|
||||||
|
public function get_total_pending_count() : int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next batch of items that need to be processed.
|
||||||
|
*
|
||||||
|
* A batch item can be anything needed to identify the actual processing to be done,
|
||||||
|
* but whenever possible items should be numbers (e.g. database record ids)
|
||||||
|
* or at least strings, to ease troubleshooting and logging in case of problems.
|
||||||
|
*
|
||||||
|
* The size of the batch returned can be less than $size if there aren't that
|
||||||
|
* many items pending processing (and it can be zero if there isn't anything to process),
|
||||||
|
* but the size should always be consistent with what 'get_total_pending_count' returns
|
||||||
|
* (i.e. the size of the returned batch shouldn't be larger than the pending items count).
|
||||||
|
*
|
||||||
|
* @param int $size Maximum size of the batch to be returned.
|
||||||
|
*
|
||||||
|
* @return array Batch of items to process, containing $size or less items.
|
||||||
|
*/
|
||||||
|
public function get_next_batch_to_process( int $size ) : array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process data for the supplied batch.
|
||||||
|
*
|
||||||
|
* This method should be prepared to receive items that don't actually need processing
|
||||||
|
* (because they have been processed before) and ignore them, but if at least
|
||||||
|
* one of the batch items that actually need processing can't be processed, an exception should be thrown.
|
||||||
|
*
|
||||||
|
* Once an item has been processed it shouldn't be counted in 'get_total_pending_count'
|
||||||
|
* nor included in 'get_next_batch_to_process' anymore (unless something happens that causes it
|
||||||
|
* to actually require further processing).
|
||||||
|
*
|
||||||
|
* @throw \Exception Something went wrong while processing the batch.
|
||||||
|
*
|
||||||
|
* @param array $batch Batch to process, as returned by 'get_next_batch_to_process'.
|
||||||
|
*/
|
||||||
|
public function process_batch( array $batch ): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default (preferred) batch size to pass to 'get_next_batch_to_process'.
|
||||||
|
* The controller will pass this size unless it's externally configured
|
||||||
|
* to use a different size.
|
||||||
|
*
|
||||||
|
* @return int Default batch size.
|
||||||
|
*/
|
||||||
|
public function get_default_batch_size() : int;
|
||||||
|
}
|
@@ -96,7 +96,7 @@ class WC_Product_Variable_Subscription_Legacy extends WC_Product_Variable_Subscr
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function get_variation_price( $min_or_max = 'min', $display = false ) {
|
public function get_variation_price( $min_or_max = 'min', $display = false ) {
|
||||||
$variation_id = get_post_meta( $this->id, '_' . $min_or_max . '_price_variation_id', true );
|
$variation_id = $this->get_meta( '_' . $min_or_max . '_price_variation_id', true );
|
||||||
|
|
||||||
if ( $display ) {
|
if ( $display ) {
|
||||||
if ( $variation = wc_get_product( $variation_id ) ) {
|
if ( $variation = wc_get_product( $variation_id ) ) {
|
||||||
@@ -109,7 +109,7 @@ class WC_Product_Variable_Subscription_Legacy extends WC_Product_Variable_Subscr
|
|||||||
$price = '';
|
$price = '';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$price = get_post_meta( $variation_id, '_price', true );
|
$price = $this->get_meta( '_price', true );
|
||||||
}
|
}
|
||||||
|
|
||||||
return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $display );
|
return apply_filters( 'woocommerce_get_variation_price', $price, $this, $min_or_max, $display );
|
||||||
@@ -299,7 +299,7 @@ class WC_Product_Variable_Subscription_Legacy extends WC_Product_Variable_Subscr
|
|||||||
$price .= wcs_get_price_html_from_text( $this );
|
$price .= wcs_get_price_html_from_text( $this );
|
||||||
}
|
}
|
||||||
|
|
||||||
$variation_id = get_post_meta( $this->id, '_min_price_variation_id', true );
|
$variation_id = $this->get_meta( '_min_price_variation_id', true );
|
||||||
$variation = wc_get_product( $variation_id );
|
$variation = wc_get_product( $variation_id );
|
||||||
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
|
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
|
||||||
|
|
||||||
@@ -358,7 +358,7 @@ class WC_Product_Variable_Subscription_Legacy extends WC_Product_Variable_Subscr
|
|||||||
* @return object WC_Product_Subscription or WC_Product_Subscription_Variation
|
* @return object WC_Product_Subscription or WC_Product_Subscription_Variation
|
||||||
*/
|
*/
|
||||||
function get_meta( $meta_key = '', $single = true, $context = 'view' ) {
|
function get_meta( $meta_key = '', $single = true, $context = 'view' ) {
|
||||||
return get_post_meta( $this->get_id(), $meta_key, $single );
|
return $this->get_meta( $meta_key, $single );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -474,7 +474,7 @@ class WC_Subscription_Legacy extends WC_Subscription {
|
|||||||
|
|
||||||
// The requires manual renewal prop uses boolean values but is stored as a string so needs special handling, it also needs to be handled before the checks on $this->$prop to avoid triggering __isset() & __get() magic methods for $this->requires_manual_renewal
|
// The requires manual renewal prop uses boolean values but is stored as a string so needs special handling, it also needs to be handled before the checks on $this->$prop to avoid triggering __isset() & __get() magic methods for $this->requires_manual_renewal
|
||||||
if ( 'requires_manual_renewal' === $prop ) {
|
if ( 'requires_manual_renewal' === $prop ) {
|
||||||
$value = get_post_meta( $this->get_id(), '_' . $prop, true );
|
$value = $this->get_meta( '_' . $prop, true );
|
||||||
|
|
||||||
if ( 'false' === $value || '' === $value ) {
|
if ( 'false' === $value || '' === $value ) {
|
||||||
$value = false;
|
$value = false;
|
||||||
@@ -482,7 +482,7 @@ class WC_Subscription_Legacy extends WC_Subscription {
|
|||||||
$value = true;
|
$value = true;
|
||||||
}
|
}
|
||||||
} elseif ( ! isset( $this->$prop ) || empty( $this->$prop ) ) {
|
} elseif ( ! isset( $this->$prop ) || empty( $this->$prop ) ) {
|
||||||
$value = get_post_meta( $this->get_id(), '_' . $prop, true );
|
$value = $this->get_meta( '_' . $prop, true );
|
||||||
} else {
|
} else {
|
||||||
$value = $this->$prop;
|
$value = $this->$prop;
|
||||||
}
|
}
|
||||||
|
@@ -16,128 +16,60 @@
|
|||||||
*/
|
*/
|
||||||
class WC_Subscriptions_Upgrader {
|
class WC_Subscriptions_Upgrader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string The database version of Subscriptions.
|
||||||
|
*/
|
||||||
private static $active_version;
|
private static $active_version;
|
||||||
|
|
||||||
private static $upgrade_limit_hooks;
|
/**
|
||||||
|
* @var string The minimum supported version that this class can upgrade from.
|
||||||
private static $upgrade_limit_subscriptions;
|
*/
|
||||||
|
private static $minimum_supported_version = '3.0';
|
||||||
private static $about_page_url;
|
|
||||||
|
|
||||||
private static $old_subscription_count = null;
|
|
||||||
|
|
||||||
public static $is_wc_version_2 = false;
|
|
||||||
|
|
||||||
public static $updated_to_wc_2_0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array An array of WCS_Background_Updater objects used to run upgrade scripts in the background.
|
* @var array An array of WCS_Background_Updater objects used to run upgrade scripts in the background.
|
||||||
*/
|
*/
|
||||||
protected static $background_updaters = array();
|
protected static $background_updaters = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deprecated variables.
|
||||||
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
|
*/
|
||||||
|
public static $is_wc_version_2 = false;
|
||||||
|
public static $updated_to_wc_2_0;
|
||||||
|
private static $upgrade_limit_subscriptions;
|
||||||
|
private static $about_page_url;
|
||||||
|
private static $old_subscription_count = null;
|
||||||
|
private static $upgrade_limit_hooks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks upgrade function to init.
|
* Hooks upgrade function to init.
|
||||||
*
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
|
||||||
*/
|
*/
|
||||||
public static function init() {
|
public static function init() {
|
||||||
|
|
||||||
self::$active_version = get_option( WC_Subscriptions_Admin::$option_prefix . '_active_version', '0' );
|
self::$active_version = get_option( WC_Subscriptions_Admin::$option_prefix . '_active_version', '0' );
|
||||||
|
|
||||||
self::$is_wc_version_2 = version_compare( get_option( 'woocommerce_db_version' ), '2.0', '>=' );
|
|
||||||
|
|
||||||
self::$about_page_url = admin_url( 'admin.php?page=wc-admin' );
|
self::$about_page_url = admin_url( 'admin.php?page=wc-admin' );
|
||||||
|
$version_out_of_date = version_compare( self::$active_version, WC_Subscriptions_Core_Plugin::instance()->get_library_version(), '<' );
|
||||||
|
|
||||||
$version_out_of_date = version_compare( self::$active_version, WC_Subscriptions_Core_Plugin::instance()->get_library_version(), '<' );
|
// Show warning that upgrades are no longer supported.
|
||||||
|
if ( '0' !== self::$active_version && version_compare( self::$active_version, self::$minimum_supported_version, '<=' ) ) {
|
||||||
// Set the cron lock on every request with an out of date version, regardless of authentication level, as we can only lock cron for up to 10 minutes at a time, but we need to keep it locked until the upgrade is complete, regardless of who is browsing the site
|
add_action(
|
||||||
if ( $version_out_of_date ) {
|
'admin_notices',
|
||||||
self::set_cron_lock();
|
function () {
|
||||||
}
|
self::show_unsupported_upgrade_path_notice();
|
||||||
|
|
||||||
if ( isset( $_POST['action'] ) && 'wcs_upgrade' == $_POST['action'] ) { // We're checking for CSRF in ajax_upgrade
|
|
||||||
|
|
||||||
add_action( 'wp_ajax_wcs_upgrade', __CLASS__ . '::ajax_upgrade', 10 );
|
|
||||||
|
|
||||||
} elseif ( @current_user_can( 'activate_plugins' ) ) {
|
|
||||||
|
|
||||||
if ( isset( $_GET['wcs_upgrade_step'] ) || $version_out_of_date ) {
|
|
||||||
|
|
||||||
$is_upgrading = get_option( 'wc_subscriptions_is_upgrading', false );
|
|
||||||
|
|
||||||
// Check if we've exceeded the 2 minute upgrade window we use for blocking upgrades (we could seemingly use transients here to get the check for free if transients were guaranteed to exist: http://journal.rmccue.io/296/youre-using-transients-wrong/)
|
|
||||||
if ( false !== $is_upgrading && $is_upgrading < gmdate( 'U' ) ) {
|
|
||||||
$is_upgrading = false;
|
|
||||||
delete_option( 'wc_subscriptions_is_upgrading' );
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
if ( false !== $is_upgrading ) {
|
return;
|
||||||
|
|
||||||
add_action( 'init', __CLASS__ . '::upgrade_in_progress_notice', 11 );
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Run upgrades as soon as admin hits site
|
|
||||||
add_action( 'wp_loaded', __CLASS__ . '::upgrade', 11 );
|
|
||||||
|
|
||||||
}
|
|
||||||
} elseif ( is_admin() && isset( $_GET['page'] ) && 'wcs-about' == $_GET['page'] ) {
|
|
||||||
|
|
||||||
add_action( 'admin_menu', __CLASS__ . '::updated_welcome_page' );
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// While the upgrade is in progress, we need to block PayPal IPN messages to avoid renewals failing to process
|
if ( @current_user_can( 'activate_plugins' ) && $version_out_of_date ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors
|
||||||
add_action( 'woocommerce_api_wc_gateway_paypal', __CLASS__ . '::maybe_block_paypal_ipn', 0 );
|
// Run upgrades as soon as admin hits site
|
||||||
|
add_action( 'wp_loaded', [ __CLASS__, 'upgrade' ], 11 );
|
||||||
// Sometimes redirect to the Welcome/About page after an upgrade
|
|
||||||
add_action( 'woocommerce_subscriptions_upgraded', __CLASS__ . '::maybe_redirect_after_upgrade_complete', 100, 2 );
|
|
||||||
|
|
||||||
add_action( 'wcs_repair_end_of_prepaid_term_actions', __CLASS__ . '::repair_end_of_prepaid_term_actions' );
|
|
||||||
|
|
||||||
add_action( 'wcs_repair_subscriptions_containing_synced_variations', __CLASS__ . '::repair_subscription_contains_sync_meta' );
|
|
||||||
|
|
||||||
// When WC is updated from a version prior to 3.0 to a version after 3.0, add subscription address indexes. Must be hooked on before WC runs its updates, which occur on priority 5.
|
|
||||||
add_action( 'init', array( __CLASS__, 'maybe_add_subscription_address_indexes' ), 2 );
|
|
||||||
|
|
||||||
add_action( 'admin_notices', array( __CLASS__, 'maybe_display_external_object_cache_warning' ) );
|
|
||||||
|
|
||||||
add_action( 'init', array( __CLASS__, 'initialise_background_updaters' ), 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set limits on the number of items to upgrade at any one time based on the size of the site.
|
|
||||||
*
|
|
||||||
* The size of subscription at the time the upgrade is started is used to determine the batch size.
|
|
||||||
*
|
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
|
||||||
*/
|
|
||||||
protected static function set_upgrade_limits() {
|
|
||||||
|
|
||||||
$total_initial_subscription_count = self::get_total_subscription_count( true );
|
|
||||||
|
|
||||||
if ( $total_initial_subscription_count > 5000 ) {
|
|
||||||
$base_upgrade_limit = 20;
|
|
||||||
} elseif ( $total_initial_subscription_count > 1500 ) {
|
|
||||||
$base_upgrade_limit = 30;
|
|
||||||
} else {
|
|
||||||
$base_upgrade_limit = 50;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self::$upgrade_limit_hooks = apply_filters( 'woocommerce_subscriptions_hooks_to_upgrade', $base_upgrade_limit * 5 );
|
add_action( 'init', [ __CLASS__, 'initialise_background_updaters' ], 0 );
|
||||||
self::$upgrade_limit_subscriptions = apply_filters( 'woocommerce_subscriptions_to_upgrade', $base_upgrade_limit );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to block WP-Cron until upgrading finishes. spawn_cron() will only let us steal the lock for 10 minutes into the future, so
|
|
||||||
* we can actually only block it for 9 minutes confidently. But as long as the upgrade process continues, the lock will remain.
|
|
||||||
*
|
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
|
||||||
*/
|
|
||||||
protected static function set_cron_lock() {
|
|
||||||
delete_transient( 'doing_cron' );
|
|
||||||
set_transient( 'doing_cron', sprintf( '%.22F', 9 * MINUTE_IN_SECONDS + microtime( true ) ), 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -148,8 +80,6 @@ class WC_Subscriptions_Upgrader {
|
|||||||
public static function upgrade() {
|
public static function upgrade() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
self::set_upgrade_limits();
|
|
||||||
|
|
||||||
update_option( WC_Subscriptions_Admin::$option_prefix . '_previous_version', self::$active_version );
|
update_option( WC_Subscriptions_Admin::$option_prefix . '_previous_version', self::$active_version );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -157,9 +87,8 @@ class WC_Subscriptions_Upgrader {
|
|||||||
*/
|
*/
|
||||||
do_action( 'woocommerce_subscriptions_before_upgrade', WC_Subscriptions_Core_Plugin::instance()->get_library_version(), self::$active_version );
|
do_action( 'woocommerce_subscriptions_before_upgrade', WC_Subscriptions_Core_Plugin::instance()->get_library_version(), self::$active_version );
|
||||||
|
|
||||||
// Update the hold stock notification to be one week (if it's still at the default 60 minutes) to prevent cancelling subscriptions using manual renewals and payment methods that can take more than 1 hour (i.e. PayPal eCheck)
|
if ( '0' === self::$active_version ) {
|
||||||
if ( '0' == self::$active_version || version_compare( self::$active_version, '1.4', '<' ) ) {
|
// Update the hold stock notification to be one week (if it's still at the default 60 minutes) to prevent cancelling subscriptions using manual renewals and payment methods that can take more than 1 hour (i.e. PayPal eCheck)
|
||||||
|
|
||||||
$hold_stock_duration = get_option( 'woocommerce_hold_stock_minutes' );
|
$hold_stock_duration = get_option( 'woocommerce_hold_stock_minutes' );
|
||||||
|
|
||||||
if ( 60 == $hold_stock_duration ) {
|
if ( 60 == $hold_stock_duration ) {
|
||||||
@@ -169,81 +98,13 @@ class WC_Subscriptions_Upgrader {
|
|||||||
// Allow products & subscriptions to be purchased in the same transaction
|
// Allow products & subscriptions to be purchased in the same transaction
|
||||||
update_option( 'woocommerce_subscriptions_multiple_purchase', 'yes' );
|
update_option( 'woocommerce_subscriptions_multiple_purchase', 'yes' );
|
||||||
|
|
||||||
}
|
// Keep track of site url to prevent duplicate payments from staging sites, first added in 1.3.8 & updated with 1.4.2 to work with WP Engine staging sites
|
||||||
|
|
||||||
// Keep track of site url to prevent duplicate payments from staging sites, first added in 1.3.8 & updated with 1.4.2 to work with WP Engine staging sites
|
|
||||||
if ( '0' == self::$active_version || version_compare( self::$active_version, '1.4.2', '<' ) ) {
|
|
||||||
WCS_Staging::set_duplicate_site_url_lock();
|
WCS_Staging::set_duplicate_site_url_lock();
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate products, WP-Cron hooks and subscriptions to the latest architecture, via Ajax
|
// Upon installing for the first time, enable or disable PayPal Standard for Subscriptions.
|
||||||
if ( '0' != self::$active_version && version_compare( self::$active_version, '2.0', '<' ) ) {
|
|
||||||
// Delete old cron locks
|
|
||||||
$deleted_rows = $wpdb->query( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE 'wcs\_blocker\_%'" );
|
|
||||||
|
|
||||||
WCS_Upgrade_Logger::add( sprintf( 'Deleted %d rows of "wcs_blocker_"', $deleted_rows ) );
|
|
||||||
|
|
||||||
self::ajax_upgrade_handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repair incorrect dates set when upgrading with 2.0.0
|
|
||||||
if ( version_compare( self::$active_version, '2.0.0', '>=' ) && version_compare( self::$active_version, '2.0.2', '<' ) && self::migrated_subscription_count() > 0 ) {
|
|
||||||
self::ajax_upgrade_handler();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( '0' != self::$active_version && version_compare( self::$active_version, '2.1.0', '<' ) ) {
|
|
||||||
|
|
||||||
// Delete cached subscription length ranges to force an update with 2.1
|
|
||||||
WC_Subscriptions_Core_Plugin::instance()->cache->delete_cached( 'wcs-sub-ranges-' . get_locale() );
|
|
||||||
WCS_Upgrade_Logger::add( 'v2.1: Deleted cached subscription ranges.' );
|
|
||||||
WCS_Upgrade_2_1::set_cancelled_dates();
|
|
||||||
|
|
||||||
// Schedule report cache updates in the hopes that the data is ready and waiting for the store owner the first time they visit the reports pages
|
|
||||||
do_action( 'woocommerce_subscriptions_reports_schedule_cache_updates' );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repair missing end_of_prepaid_term scheduled actions
|
|
||||||
if ( version_compare( self::$active_version, '2.2.0', '>=' ) && version_compare( self::$active_version, '2.2.7', '<' ) ) {
|
|
||||||
WCS_Upgrade_2_2_7::schedule_end_of_prepaid_term_repair();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repair missing _contains_synced_subscription post meta
|
|
||||||
if ( version_compare( get_option( 'woocommerce_db_version' ), '3.0', '>=' ) && version_compare( self::$active_version, '2.2.0', '>=' ) && version_compare( self::$active_version, '2.2.9', '<' ) ) {
|
|
||||||
WCS_Upgrade_2_2_9::schedule_repair();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repair subscriptions suspended via PayPal.
|
|
||||||
if ( version_compare( self::$active_version, '2.1.4', '>=' ) && version_compare( self::$active_version, '2.3.0', '<' ) ) {
|
|
||||||
self::$background_updaters['2.3']['suspended_paypal_repair']->schedule_repair();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the store is running WC 3.0, repair subscriptions with missing address indexes.
|
|
||||||
if ( '0' !== self::$active_version && version_compare( self::$active_version, '2.3.0', '<' ) && version_compare( WC()->version, '3.0', '>=' ) ) {
|
|
||||||
self::$background_updaters['2.3']['address_indexes_repair']->schedule_repair();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( version_compare( self::$active_version, '2.3.0', '>=' ) && version_compare( self::$active_version, '2.3.3', '<' ) && wp_using_ext_object_cache() ) {
|
|
||||||
$has_transient_cache = $wpdb->get_var( "SELECT option_id FROM {$wpdb->prefix}options WHERE option_name LIKE '_transient_wcs-related-orders-to%' OR option_name LIKE '_transient_wcs_user_subscriptions_%' LIMIT 1;" );
|
|
||||||
|
|
||||||
if ( ! empty( $has_transient_cache ) ) {
|
|
||||||
update_option( 'wcs_display_2_3_3_warning', 'yes' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( version_compare( self::$active_version, '2.4.0', '<' ) ) {
|
|
||||||
self::$background_updaters['2.4']['start_date_metadata']->schedule_repair();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upon upgrading or installing 2.5.0 for the first time, enable or disable PayPal Standard for Subscriptions.
|
|
||||||
if ( version_compare( self::$active_version, '2.5.0', '<' ) ) {
|
|
||||||
WCS_PayPal::set_enabled_for_subscriptions_default();
|
WCS_PayPal::set_enabled_for_subscriptions_default();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upon upgrading to 2.6.0 from a version after 2.2.0, schedule missing _has_trial line item meta repair.
|
|
||||||
if ( version_compare( self::$active_version, '2.6.0', '<' ) && version_compare( self::$active_version, '2.2.0', '>=' ) ) {
|
|
||||||
self::$background_updaters['2.6']['has_trial_item_meta']->schedule_repair();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete old subscription period string ranges transients.
|
// Delete old subscription period string ranges transients.
|
||||||
if ( version_compare( self::$active_version, '3.0.10', '<' ) ) {
|
if ( version_compare( self::$active_version, '3.0.10', '<' ) ) {
|
||||||
$deleted_rows = $wpdb->query( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE '_transient_timeout_wcs-sub-ranges-%' OR `option_name` LIKE '_transient_wcs-sub-ranges-%'" );
|
$deleted_rows = $wpdb->query( "DELETE FROM {$wpdb->options} WHERE `option_name` LIKE '_transient_timeout_wcs-sub-ranges-%' OR `option_name` LIKE '_transient_wcs-sub-ranges-%'" );
|
||||||
@@ -272,27 +133,185 @@ class WC_Subscriptions_Upgrader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When an upgrade is complete, set the active version, delete the transient locking upgrade and fire a hook.
|
* When an upgrade is complete, set the active version and fire a hook.
|
||||||
*
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
|
||||||
*/
|
*/
|
||||||
public static function upgrade_complete() {
|
public static function upgrade_complete() {
|
||||||
|
|
||||||
update_option( WC_Subscriptions_Admin::$option_prefix . '_active_version', WC_Subscriptions_Core_Plugin::instance()->get_library_version() );
|
update_option( WC_Subscriptions_Admin::$option_prefix . '_active_version', WC_Subscriptions_Core_Plugin::instance()->get_library_version() );
|
||||||
|
|
||||||
delete_transient( 'doing_cron' );
|
|
||||||
|
|
||||||
delete_option( 'wc_subscriptions_is_upgrading' );
|
|
||||||
|
|
||||||
do_action( 'woocommerce_subscriptions_upgraded', WC_Subscriptions_Core_Plugin::instance()->get_library_version(), self::$active_version );
|
do_action( 'woocommerce_subscriptions_upgraded', WC_Subscriptions_Core_Plugin::instance()->get_library_version(), self::$active_version );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redirect to the Subscriptions major version Welcome/About page for major version updates
|
* Load and initialise the background updaters.
|
||||||
|
*
|
||||||
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.4.0
|
||||||
|
*/
|
||||||
|
public static function initialise_background_updaters() {
|
||||||
|
$logger = new WC_logger();
|
||||||
|
self::$background_updaters['3.1']['subtracted_base_tax_repair'] = new WCS_Repair_Subtracted_Base_Tax_Line_Item_Meta( $logger );
|
||||||
|
|
||||||
|
// Init the updaters
|
||||||
|
foreach ( self::$background_updaters as $version => $updaters ) {
|
||||||
|
foreach ( $updaters as $updater ) {
|
||||||
|
$updater->init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repair a single item's subtracted base tax meta.
|
||||||
|
*
|
||||||
|
* @since 3.1.0
|
||||||
|
* @param int $item_id The ID of the item which needs repairing.
|
||||||
|
*/
|
||||||
|
public static function repair_subtracted_base_taxes( $item_id ) {
|
||||||
|
if ( ! isset( self::$background_updaters['3.1']['subtracted_base_tax_repair'] ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$background_updaters['3.1']['subtracted_base_tax_repair']->repair_item( $item_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show an admin notice if the store is upgrading from a Subscriptions version that's no longer supported.
|
||||||
|
*
|
||||||
|
* @since 7.7.0
|
||||||
|
*/
|
||||||
|
private static function show_unsupported_upgrade_path_notice() {
|
||||||
|
echo '<div class="notice notice-error"><p>' .
|
||||||
|
esc_html(
|
||||||
|
__(
|
||||||
|
'A database upgrade is required to use Subscriptions. Upgrades from the previously installed version is no longer supported. You will need to install an older version of WooCommerce Subscriptions or WooCommerce Payments to proceed with the upgrade before you can use a newer version.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
)
|
||||||
|
) .
|
||||||
|
'</p></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deprecated Functions */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the WC 3.5.0 upgrade routine that moves customer IDs from post metadata to the 'post_author' column.
|
||||||
|
*
|
||||||
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.4.0
|
||||||
|
* @deprecated 1.0.0 - Migrated from WooCommerce Subscriptions v2.5.0
|
||||||
|
*/
|
||||||
|
public static function maybe_update_subscription_post_author() {
|
||||||
|
wcs_deprecated_function( __METHOD__, '2.5.0' );
|
||||||
|
|
||||||
|
if ( version_compare( WC()->version, '3.5.0', '<' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If WC hasn't run the update routine yet we can hook into theirs to update subscriptions, otherwise we'll need to schedule our own update.
|
||||||
|
if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '<' ) ) {
|
||||||
|
self::$background_updaters['2.4']['subscription_post_author']->hook_into_wc_350_update();
|
||||||
|
} elseif ( version_compare( self::$active_version, '2.4.0', '<' ) ) {
|
||||||
|
self::$background_updaters['2.4']['subscription_post_author']->schedule_repair();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to check if a user ID is greater than the last user upgraded to version 1.4.
|
||||||
|
*
|
||||||
|
* Needs to be a separate function so that it can use a static variable (and therefore avoid calling get_option() thousands
|
||||||
|
* of times when iterating over thousands of users).
|
||||||
|
*
|
||||||
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
|
||||||
|
*/
|
||||||
|
public static function is_user_upgraded_to_1_4( $user_id ) {
|
||||||
|
_deprecated_function( __METHOD__, '2.0', 'WCS_Upgrade_1_4::is_user_upgraded( $user_id )' );
|
||||||
|
return WCS_Upgrade_1_4::is_user_upgraded( $user_id );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display an admin notice if the database version is greater than the active version of the plugin by at least one minor release (eg 1.1 and 1.0).
|
||||||
|
*
|
||||||
|
* @since 2.3.0
|
||||||
|
* @deprecated 1.2.0
|
||||||
|
*/
|
||||||
|
public static function maybe_add_downgrade_notice() {
|
||||||
|
wcs_deprecated_function( __METHOD__, '1.2.0' );
|
||||||
|
|
||||||
|
// If there's no downgrade, exit early. self::$active_version is a bit of a misnomer here but in an upgrade context it refers to the database version of the plugin.
|
||||||
|
if ( ! version_compare( wcs_get_minor_version_string( self::$active_version ), wcs_get_minor_version_string( WC_Subscriptions_Core_Plugin::instance()->get_library_version() ), '>' ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$admin_notice = new WCS_Admin_Notice( 'error' );
|
||||||
|
$admin_notice->set_simple_content(
|
||||||
|
sprintf(
|
||||||
|
// translators: 1-2: opening/closing <strong> tags, 3: active version of Subscriptions, 4: current version of Subscriptions, 5-6: opening/closing tags linked to ticket form, 7-8: opening/closing tags linked to documentation.
|
||||||
|
esc_html__( '%1$sWarning!%2$s It appears that you have downgraded %1$sWooCommerce Subscriptions%2$s from %3$s to %4$s. Downgrading the plugin in this way may cause issues. Please update to %3$s or higher, or %5$sopen a new support ticket%6$s for further assistance. %7$sLearn more »%8$s', 'woocommerce-subscriptions' ),
|
||||||
|
'<strong>',
|
||||||
|
'</strong>',
|
||||||
|
'<code>' . self::$active_version . '</code>',
|
||||||
|
'<code>' . WC_Subscriptions_Core_Plugin::instance()->get_library_version() . '</code>',
|
||||||
|
'<a href="https://woocommerce.com/my-account/marketplace-ticket-form/" target="_blank">',
|
||||||
|
'</a>',
|
||||||
|
'<a href="https://woocommerce.com/document/subscriptions/upgrade-instructions/#section-12" target="_blank">',
|
||||||
|
'</a>'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$admin_notice->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deprecated functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set limits on the number of items to upgrade at any one time based on the size of the site.
|
||||||
|
*
|
||||||
|
* The size of subscription at the time the upgrade is started is used to determine the batch size.
|
||||||
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0 - Upgrade limits were used when the upgrade process used AJAX.
|
||||||
|
*
|
||||||
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
|
*/
|
||||||
|
protected static function set_upgrade_limits() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
|
$total_initial_subscription_count = self::get_total_subscription_count( true );
|
||||||
|
|
||||||
|
if ( $total_initial_subscription_count > 5000 ) {
|
||||||
|
$base_upgrade_limit = 20;
|
||||||
|
} elseif ( $total_initial_subscription_count > 1500 ) {
|
||||||
|
$base_upgrade_limit = 30;
|
||||||
|
} else {
|
||||||
|
$base_upgrade_limit = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$upgrade_limit_hooks = apply_filters( 'woocommerce_subscriptions_hooks_to_upgrade', $base_upgrade_limit * 5 );
|
||||||
|
self::$upgrade_limit_subscriptions = apply_filters( 'woocommerce_subscriptions_to_upgrade', $base_upgrade_limit );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to block WP-Cron until upgrading finishes. spawn_cron() will only let us steal the lock for 10 minutes into the future, so
|
||||||
|
* we can actually only block it for 9 minutes confidently. But as long as the upgrade process continues, the lock will remain.
|
||||||
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0 Cron lock was required for more intensive upgrades prior to v3.0
|
||||||
|
*
|
||||||
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
|
*/
|
||||||
|
protected static function set_cron_lock() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
delete_transient( 'doing_cron' );
|
||||||
|
set_transient( 'doing_cron', sprintf( '%.22F', 9 * MINUTE_IN_SECONDS + microtime( true ) ), 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect to the Subscriptions major version Welcome/About page for major version updates.
|
||||||
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
*
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.1
|
||||||
*/
|
*/
|
||||||
public static function maybe_redirect_after_upgrade_complete( $current_version, $previously_active_version ) {
|
public static function maybe_redirect_after_upgrade_complete( $current_version, $previously_active_version ) {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
if ( version_compare( $previously_active_version, '2.1.0', '<' ) && version_compare( $current_version, '2.1.0', '>=' ) && version_compare( $current_version, '2.2.0', '<' ) ) {
|
if ( version_compare( $previously_active_version, '2.1.0', '<' ) && version_compare( $current_version, '2.1.0', '>=' ) && version_compare( $current_version, '2.2.0', '<' ) ) {
|
||||||
wp_safe_redirect( self::$about_page_url );
|
wp_safe_redirect( self::$about_page_url );
|
||||||
exit();
|
exit();
|
||||||
@@ -303,9 +322,12 @@ class WC_Subscriptions_Upgrader {
|
|||||||
* Add support for quantities for subscriptions.
|
* Add support for quantities for subscriptions.
|
||||||
* Update all current subscription wp_cron tasks to the new action-scheduler system.
|
* Update all current subscription wp_cron tasks to the new action-scheduler system.
|
||||||
*
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
*/
|
*/
|
||||||
private static function ajax_upgrade_handler() {
|
private static function ajax_upgrade_handler() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
$_GET['wcs_upgrade_step'] = ( ! isset( $_GET['wcs_upgrade_step'] ) ) ? 0 : $_GET['wcs_upgrade_step'];
|
$_GET['wcs_upgrade_step'] = ( ! isset( $_GET['wcs_upgrade_step'] ) ) ? 0 : $_GET['wcs_upgrade_step'];
|
||||||
|
|
||||||
@@ -332,10 +354,13 @@ class WC_Subscriptions_Upgrader {
|
|||||||
* Also set all existing subscriptions to "sold individually" to maintain previous behavior
|
* Also set all existing subscriptions to "sold individually" to maintain previous behavior
|
||||||
* for existing subscription products before the subscription quantities feature was enabled..
|
* for existing subscription products before the subscription quantities feature was enabled..
|
||||||
*
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0 - This function is only used when upgrading from versions less than v3.0.
|
||||||
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.5
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.5
|
||||||
*/
|
*/
|
||||||
public static function ajax_upgrade() {
|
public static function ajax_upgrade() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscription-core 7.7.0' );
|
||||||
|
|
||||||
check_admin_referer( 'wcs_upgrade_process', 'nonce' );
|
check_admin_referer( 'wcs_upgrade_process', 'nonce' );
|
||||||
|
|
||||||
@@ -477,9 +502,11 @@ class WC_Subscriptions_Upgrader {
|
|||||||
/**
|
/**
|
||||||
* Handle upgrades for really old versions.
|
* Handle upgrades for really old versions.
|
||||||
*
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
*/
|
*/
|
||||||
private static function upgrade_really_old_versions() {
|
private static function upgrade_really_old_versions() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
if ( '0' != self::$active_version && version_compare( self::$active_version, '1.2', '<' ) ) {
|
if ( '0' != self::$active_version && version_compare( self::$active_version, '1.2', '<' ) ) {
|
||||||
WCS_Upgrade_1_2::init();
|
WCS_Upgrade_1_2::init();
|
||||||
@@ -509,10 +536,14 @@ class WC_Subscriptions_Upgrader {
|
|||||||
* Version 1.2 introduced child renewal orders to keep a record of each completed subscription
|
* Version 1.2 introduced child renewal orders to keep a record of each completed subscription
|
||||||
* payment. Before 1.2, these orders did not exist, so this function creates them.
|
* payment. Before 1.2, these orders did not exist, so this function creates them.
|
||||||
*
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
|
||||||
*/
|
*/
|
||||||
private static function generate_renewal_orders() {
|
private static function generate_renewal_orders() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
$woocommerce = WC();
|
$woocommerce = WC();
|
||||||
|
|
||||||
$subscriptions_grouped_by_user = WC_Subscriptions_Manager::get_all_users_subscriptions();
|
$subscriptions_grouped_by_user = WC_Subscriptions_Manager::get_all_users_subscriptions();
|
||||||
@@ -578,27 +609,18 @@ class WC_Subscriptions_Upgrader {
|
|||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.2
|
||||||
*/
|
*/
|
||||||
public static function display_database_upgrade_helper() {
|
public static function display_database_upgrade_helper() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
wp_register_style( 'wcs-upgrade', WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory_url( 'assets/css/wcs-upgrade.css' ) );
|
wp_register_style( 'wcs-upgrade', WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory_url( 'assets/css/wcs-upgrade.css' ) );
|
||||||
wp_register_script( 'wcs-upgrade', WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory_url( 'assets/js/wcs-upgrade.js' ), 'jquery' );
|
wp_register_script( 'wcs-upgrade', WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory_url( 'assets/js/wcs-upgrade.js' ), 'jquery' );
|
||||||
|
|
||||||
if ( version_compare( self::$active_version, '2.0.0', '<' ) ) {
|
$subscription_count = 0;
|
||||||
// We're running the 2.0 upgrade routine
|
|
||||||
$subscription_count = self::get_total_subscription_count();
|
|
||||||
} elseif ( version_compare( self::$active_version, '2.0.0', '>=' ) && version_compare( self::$active_version, '2.0.2', '<' ) ) {
|
|
||||||
// We're running the 2.0.2 repair routine
|
|
||||||
$subscription_counts = wp_count_posts( 'shop_subscription' );
|
|
||||||
$subscription_count = array_sum( (array) $subscription_counts ) - $subscription_counts->trash - $subscription_counts->{'auto-draft'};
|
|
||||||
} else {
|
|
||||||
// How did we get here?
|
|
||||||
$subscription_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$script_data = array(
|
$script_data = array(
|
||||||
'really_old_version' => ( version_compare( self::$active_version, '1.4', '<' ) ) ? 'true' : 'false',
|
'really_old_version' => 'false',
|
||||||
'upgrade_to_1_5' => ( version_compare( self::$active_version, '1.5', '<' ) ) ? 'true' : 'false',
|
'upgrade_to_1_5' => false,
|
||||||
'upgrade_to_2_0' => ( version_compare( self::$active_version, '2.0.0', '<' ) ) ? 'true' : 'false',
|
'upgrade_to_2_0' => false,
|
||||||
'repair_2_0' => ( version_compare( self::$active_version, '2.0.0', '>=' ) && version_compare( self::$active_version, '2.0.2', '<' ) ) ? 'true' : 'false',
|
'repair_2_0' => false,
|
||||||
'hooks_per_request' => self::$upgrade_limit_hooks,
|
'hooks_per_request' => self::$upgrade_limit_hooks,
|
||||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||||
'upgrade_nonce' => wp_create_nonce( 'wcs_upgrade_process' ),
|
'upgrade_nonce' => wp_create_nonce( 'wcs_upgrade_process' ),
|
||||||
@@ -637,9 +659,13 @@ class WC_Subscriptions_Upgrader {
|
|||||||
* upgrade process, and how many subscriptions per request can typically be updated given the amount of memory
|
* upgrade process, and how many subscriptions per request can typically be updated given the amount of memory
|
||||||
* allocated to PHP.
|
* allocated to PHP.
|
||||||
*
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0 - We know longer use a notice or pages to display upgrade progress.
|
||||||
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
|
||||||
*/
|
*/
|
||||||
public static function upgrade_in_progress_notice() {
|
public static function upgrade_in_progress_notice() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
include_once( dirname( __FILE__ ) . '/templates/wcs-upgrade-in-progress.php' );
|
include_once( dirname( __FILE__ ) . '/templates/wcs-upgrade-in-progress.php' );
|
||||||
WCS_Upgrade_Logger::add( 'Loaded database upgrade in progress notice...' );
|
WCS_Upgrade_Logger::add( 'Loaded database upgrade in progress notice...' );
|
||||||
}
|
}
|
||||||
@@ -650,6 +676,8 @@ class WC_Subscriptions_Upgrader {
|
|||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
|
||||||
*/
|
*/
|
||||||
public static function updated_welcome_page() {
|
public static function updated_welcome_page() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
$about_page = add_dashboard_page( __( 'Welcome to WooCommerce Subscriptions 2.1', 'woocommerce-subscriptions' ), __( 'About WooCommerce Subscriptions', 'woocommerce-subscriptions' ), 'manage_options', 'wcs-about', __CLASS__ . '::about_screen' );
|
$about_page = add_dashboard_page( __( 'Welcome to WooCommerce Subscriptions 2.1', 'woocommerce-subscriptions' ), __( 'About WooCommerce Subscriptions', 'woocommerce-subscriptions' ), 'manage_options', 'wcs-about', __CLASS__ . '::about_screen' );
|
||||||
add_action( 'admin_print_styles-' . $about_page, __CLASS__ . '::admin_css' );
|
add_action( 'admin_print_styles-' . $about_page, __CLASS__ . '::admin_css' );
|
||||||
add_action( 'admin_head', __CLASS__ . '::admin_head' );
|
add_action( 'admin_head', __CLASS__ . '::admin_head' );
|
||||||
@@ -658,20 +686,21 @@ class WC_Subscriptions_Upgrader {
|
|||||||
/**
|
/**
|
||||||
* admin_css function.
|
* admin_css function.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function admin_css() {
|
public static function admin_css() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
wp_enqueue_style( 'woocommerce-subscriptions-about', WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory_url( 'assets/css/about.css' ), array(), self::$active_version );
|
wp_enqueue_style( 'woocommerce-subscriptions-about', WC_Subscriptions_Core_Plugin::instance()->get_subscriptions_core_directory_url( 'assets/css/about.css' ), array(), self::$active_version );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add styles just for this page, and remove dashboard page links.
|
* Add styles just for this page, and remove dashboard page links.
|
||||||
*
|
*
|
||||||
* @access public
|
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function admin_head() {
|
public static function admin_head() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
remove_submenu_page( 'index.php', 'wcs-about' );
|
remove_submenu_page( 'index.php', 'wcs-about' );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -679,6 +708,7 @@ class WC_Subscriptions_Upgrader {
|
|||||||
* Output the about screen.
|
* Output the about screen.
|
||||||
*/
|
*/
|
||||||
public static function about_screen() {
|
public static function about_screen() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
$active_version = self::$active_version;
|
$active_version = self::$active_version;
|
||||||
$settings_page = admin_url( 'admin.php?page=wc-settings&tab=subscriptions' );
|
$settings_page = admin_url( 'admin.php?page=wc-settings&tab=subscriptions' );
|
||||||
@@ -694,6 +724,7 @@ class WC_Subscriptions_Upgrader {
|
|||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
*/
|
*/
|
||||||
private static function get_total_subscription_count( $initial = false ) {
|
private static function get_total_subscription_count( $initial = false ) {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
if ( $initial ) {
|
if ( $initial ) {
|
||||||
|
|
||||||
@@ -721,6 +752,7 @@ class WC_Subscriptions_Upgrader {
|
|||||||
*/
|
*/
|
||||||
private static function get_total_subscription_count_query() {
|
private static function get_total_subscription_count_query() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
$query = self::get_subscription_query();
|
$query = self::get_subscription_query();
|
||||||
|
|
||||||
@@ -729,6 +761,7 @@ class WC_Subscriptions_Upgrader {
|
|||||||
return $wpdb->num_rows;
|
return $wpdb->num_rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single source of truth for the query
|
* Single source of truth for the query
|
||||||
* @param integer $limit the number of subscriptions to get
|
* @param integer $limit the number of subscriptions to get
|
||||||
@@ -736,6 +769,7 @@ class WC_Subscriptions_Upgrader {
|
|||||||
*/
|
*/
|
||||||
public static function get_subscription_query( $batch_size = null ) {
|
public static function get_subscription_query( $batch_size = null ) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
if ( null === $batch_size ) {
|
if ( null === $batch_size ) {
|
||||||
$select = 'SELECT DISTINCT items.order_item_id';
|
$select = 'SELECT DISTINCT items.order_item_id';
|
||||||
@@ -774,6 +808,7 @@ class WC_Subscriptions_Upgrader {
|
|||||||
*/
|
*/
|
||||||
protected static function migrated_subscription_count() {
|
protected static function migrated_subscription_count() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
$migrated_subscription_count = $wpdb->get_var(
|
$migrated_subscription_count = $wpdb->get_var(
|
||||||
"SELECT COUNT(DISTINCT `post_id`) FROM $wpdb->postmeta
|
"SELECT COUNT(DISTINCT `post_id`) FROM $wpdb->postmeta
|
||||||
@@ -783,6 +818,7 @@ class WC_Subscriptions_Upgrader {
|
|||||||
return $migrated_subscription_count;
|
return $migrated_subscription_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* While the upgrade is in progress, we need to block IPN messages to avoid renewals failing to process correctly.
|
* While the upgrade is in progress, we need to block IPN messages to avoid renewals failing to process correctly.
|
||||||
*
|
*
|
||||||
@@ -791,9 +827,13 @@ class WC_Subscriptions_Upgrader {
|
|||||||
*
|
*
|
||||||
* The method returns a 409 Conflict HTTP response code to indicate that the IPN is conflicting with the upgrader.
|
* The method returns a 409 Conflict HTTP response code to indicate that the IPN is conflicting with the upgrader.
|
||||||
*
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0 - We no lock down the store during subscription upgrades so we don't need to block IPNs.
|
||||||
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
*/
|
*/
|
||||||
public static function maybe_block_paypal_ipn() {
|
public static function maybe_block_paypal_ipn() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
if ( false !== get_option( 'wc_subscriptions_is_upgrading', false ) ) {
|
if ( false !== get_option( 'wc_subscriptions_is_upgrading', false ) ) {
|
||||||
WCS_Upgrade_Logger::add( '*** PayPal IPN Request blocked: ' . print_r( wp_unslash( $_POST ), true ) ); // No CSRF needed as it's from outside
|
WCS_Upgrade_Logger::add( '*** PayPal IPN Request blocked: ' . print_r( wp_unslash( $_POST ), true ) ); // No CSRF needed as it's from outside
|
||||||
wp_die( 'PayPal IPN Request Failure', 'PayPal IPN', array( 'response' => 409 ) );
|
wp_die( 'PayPal IPN Request Failure', 'PayPal IPN', array( 'response' => 409 ) );
|
||||||
@@ -802,54 +842,42 @@ class WC_Subscriptions_Upgrader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the end of prepaid term repair script.
|
* Run the end of prepaid term repair script.
|
||||||
*
|
* @deprecated subscriptions-core 7.7.0
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.7
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.7
|
||||||
*/
|
*/
|
||||||
public static function repair_end_of_prepaid_term_actions() {
|
public static function repair_end_of_prepaid_term_actions() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
WCS_Upgrade_2_2_7::repair_pending_cancelled_subscriptions();
|
WCS_Upgrade_2_2_7::repair_pending_cancelled_subscriptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repair subscriptions with missing contains_synced_subscription post meta.
|
* Repair subscriptions with missing contains_synced_subscription post meta.
|
||||||
*
|
* @deprecated subscriptions-core 7.7.0
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.9
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.2.9
|
||||||
*/
|
*/
|
||||||
public static function repair_subscription_contains_sync_meta() {
|
public static function repair_subscription_contains_sync_meta() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
WCS_Upgrade_2_2_9::repair_subscriptions_containing_synced_variations();
|
WCS_Upgrade_2_2_9::repair_subscriptions_containing_synced_variations();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When updating WC to a version after 3.0 from a version prior to 3.0, schedule the repair script to add address indexes.
|
* When updating WC to a version after 3.0 from a version prior to 3.0, schedule the repair script to add address indexes.
|
||||||
*
|
*
|
||||||
|
* @deprecated subscriptions-core 7.7.0 - Upgrading from before WC 3.0 is not supported.
|
||||||
|
*
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.0
|
||||||
*/
|
*/
|
||||||
public static function maybe_add_subscription_address_indexes() {
|
public static function maybe_add_subscription_address_indexes() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
$woocommerce_active_version = WC()->version;
|
$woocommerce_active_version = WC()->version;
|
||||||
$woocommerce_database_version = get_option( 'woocommerce_version' );
|
$woocommerce_database_version = get_option( 'woocommerce_version' );
|
||||||
|
|
||||||
if ( $woocommerce_active_version !== $woocommerce_database_version && version_compare( $woocommerce_active_version, '3.0', '>=' ) && version_compare( $woocommerce_database_version, '3.0', '<' ) ) {
|
if ( $woocommerce_active_version !== $woocommerce_database_version && version_compare( $woocommerce_active_version, '3.0', '>=' ) && version_compare( $woocommerce_database_version, '3.0', '<' ) ) {
|
||||||
self::$background_updaters['2.3']['address_indexes_repair']->schedule_repair();
|
$logger = new WC_logger();
|
||||||
}
|
$background_updater = new WCS_Repair_Subscription_Address_Indexes( $logger );
|
||||||
}
|
$background_updater->init();
|
||||||
|
$background_updater->schedule_repair();
|
||||||
/**
|
|
||||||
* Load and initialise the background updaters.
|
|
||||||
*
|
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.4.0
|
|
||||||
*/
|
|
||||||
public static function initialise_background_updaters() {
|
|
||||||
$logger = new WC_logger();
|
|
||||||
self::$background_updaters['2.3']['suspended_paypal_repair'] = new WCS_Repair_Suspended_PayPal_Subscriptions( $logger );
|
|
||||||
self::$background_updaters['2.3']['address_indexes_repair'] = new WCS_Repair_Subscription_Address_Indexes( $logger );
|
|
||||||
self::$background_updaters['2.4']['start_date_metadata'] = new WCS_Repair_Start_Date_Metadata( $logger );
|
|
||||||
self::$background_updaters['2.6']['has_trial_item_meta'] = new WCS_Repair_Line_Item_Has_Trial_Meta( $logger );
|
|
||||||
self::$background_updaters['3.1']['subtracted_base_tax_repair'] = new WCS_Repair_Subtracted_Base_Tax_Line_Item_Meta( $logger );
|
|
||||||
|
|
||||||
// Init the updaters
|
|
||||||
foreach ( self::$background_updaters as $version => $updaters ) {
|
|
||||||
foreach ( $updaters as $updater ) {
|
|
||||||
$updater->init();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,8 +889,11 @@ class WC_Subscriptions_Upgrader {
|
|||||||
*
|
*
|
||||||
* @see https://github.com/Prospress/woocommerce-subscriptions/issues/2822 for more details.
|
* @see https://github.com/Prospress/woocommerce-subscriptions/issues/2822 for more details.
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.3
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.3.3
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
*/
|
*/
|
||||||
public static function maybe_display_external_object_cache_warning() {
|
public static function maybe_display_external_object_cache_warning() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
$option_name = 'wcs_display_2_3_3_warning';
|
$option_name = 'wcs_display_2_3_3_warning';
|
||||||
$nonce = '_wcsnonce';
|
$nonce = '_wcsnonce';
|
||||||
$action = 'wcs_external_cache_warning';
|
$action = 'wcs_external_cache_warning';
|
||||||
@@ -895,83 +926,4 @@ class WC_Subscriptions_Upgrader {
|
|||||||
|
|
||||||
$admin_notice->display();
|
$admin_notice->display();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Repair a single item's subtracted base tax meta.
|
|
||||||
*
|
|
||||||
* @since 3.1.0
|
|
||||||
* @param int $item_id The ID of the item which needs repairing.
|
|
||||||
*/
|
|
||||||
public static function repair_subtracted_base_taxes( $item_id ) {
|
|
||||||
self::$background_updaters['3.1']['subtracted_base_tax_repair']->repair_item( $item_id );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Deprecated Functions */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the WC 3.5.0 upgrade routine that moves customer IDs from post metadata to the 'post_author' column.
|
|
||||||
*
|
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.4.0
|
|
||||||
* @deprecated 1.0.0 - Migrated from WooCommerce Subscriptions v2.5.0
|
|
||||||
*/
|
|
||||||
public static function maybe_update_subscription_post_author() {
|
|
||||||
wcs_deprecated_function( __METHOD__, '2.5.0' );
|
|
||||||
|
|
||||||
if ( version_compare( WC()->version, '3.5.0', '<' ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If WC hasn't run the update routine yet we can hook into theirs to update subscriptions, otherwise we'll need to schedule our own update.
|
|
||||||
if ( version_compare( get_option( 'woocommerce_db_version' ), '3.5.0', '<' ) ) {
|
|
||||||
self::$background_updaters['2.4']['subscription_post_author']->hook_into_wc_350_update();
|
|
||||||
} else if ( version_compare( self::$active_version, '2.4.0', '<' ) ) {
|
|
||||||
self::$background_updaters['2.4']['subscription_post_author']->schedule_repair();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to check if a user ID is greater than the last user upgraded to version 1.4.
|
|
||||||
*
|
|
||||||
* Needs to be a separate function so that it can use a static variable (and therefore avoid calling get_option() thousands
|
|
||||||
* of times when iterating over thousands of users).
|
|
||||||
*
|
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
|
|
||||||
*/
|
|
||||||
public static function is_user_upgraded_to_1_4( $user_id ) {
|
|
||||||
_deprecated_function( __METHOD__, '2.0', 'WCS_Upgrade_1_4::is_user_upgraded( $user_id )' );
|
|
||||||
return WCS_Upgrade_1_4::is_user_upgraded( $user_id );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display an admin notice if the database version is greater than the active version of the plugin by at least one minor release (eg 1.1 and 1.0).
|
|
||||||
*
|
|
||||||
* @since 2.3.0
|
|
||||||
* @deprecated 1.2.0
|
|
||||||
*/
|
|
||||||
public static function maybe_add_downgrade_notice() {
|
|
||||||
wcs_deprecated_function( __METHOD__, '1.2.0' );
|
|
||||||
|
|
||||||
// If there's no downgrade, exit early. self::$active_version is a bit of a misnomer here but in an upgrade context it refers to the database version of the plugin.
|
|
||||||
if ( ! version_compare( wcs_get_minor_version_string( self::$active_version ), wcs_get_minor_version_string( WC_Subscriptions_Core_Plugin::instance()->get_library_version() ), '>' ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$admin_notice = new WCS_Admin_Notice( 'error' );
|
|
||||||
$admin_notice->set_simple_content(
|
|
||||||
sprintf(
|
|
||||||
// translators: 1-2: opening/closing <strong> tags, 3: active version of Subscriptions, 4: current version of Subscriptions, 5-6: opening/closing tags linked to ticket form, 7-8: opening/closing tags linked to documentation.
|
|
||||||
esc_html__( '%1$sWarning!%2$s It appears that you have downgraded %1$sWooCommerce Subscriptions%2$s from %3$s to %4$s. Downgrading the plugin in this way may cause issues. Please update to %3$s or higher, or %5$sopen a new support ticket%6$s for further assistance. %7$sLearn more »%8$s', 'woocommerce-subscriptions' ),
|
|
||||||
'<strong>',
|
|
||||||
'</strong>',
|
|
||||||
'<code>' . self::$active_version . '</code>',
|
|
||||||
'<code>' . WC_Subscriptions_Core_Plugin::instance()->get_library_version() . '</code>',
|
|
||||||
'<a href="https://woocommerce.com/my-account/marketplace-ticket-form/" target="_blank">',
|
|
||||||
'</a>',
|
|
||||||
'<a href="https://woocommerce.com/document/subscriptions/upgrade-instructions/#section-12" target="_blank">',
|
|
||||||
'</a>'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$admin_notice->display();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Repair_2_0_2 {
|
class WCS_Repair_2_0_2 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -12,6 +12,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Repair_2_0 {
|
class WCS_Repair_2_0 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -392,7 +395,8 @@ class WCS_Repair_2_0 {
|
|||||||
public static function repair_start_date( $subscription, $item_id, $item_meta ) {
|
public static function repair_start_date( $subscription, $item_id, $item_meta ) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
$start_date = get_post_meta( $subscription['order_id'], '_paid_date', true );
|
$order = wc_get_order( $subscription['order_id'] );
|
||||||
|
$start_date = $order->get_meta( '_paid_date', true );
|
||||||
|
|
||||||
WCS_Upgrade_Logger::add( sprintf( 'Repairing start_date for order %d: Trying to use the _paid date for start date.', $subscription['order_id'] ) );
|
WCS_Upgrade_Logger::add( sprintf( 'Repairing start_date for order %d: Trying to use the _paid date for start date.', $subscription['order_id'] ) );
|
||||||
|
|
||||||
|
@@ -22,6 +22,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Repair_Line_Item_Has_Trial_Meta extends WCS_Background_Repairer {
|
class WCS_Repair_Line_Item_Has_Trial_Meta extends WCS_Background_Repairer {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -13,6 +13,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Repair_Start_Date_Metadata extends WCS_Background_Upgrader {
|
class WCS_Repair_Start_Date_Metadata extends WCS_Background_Upgrader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -16,6 +16,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Repair_Subscription_Address_Indexes extends WCS_Background_Upgrader {
|
class WCS_Repair_Subscription_Address_Indexes extends WCS_Background_Upgrader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -19,6 +19,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Repair_Suspended_PayPal_Subscriptions extends WCS_Background_Upgrader {
|
class WCS_Repair_Suspended_PayPal_Subscriptions extends WCS_Background_Upgrader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -20,10 +20,14 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
|
*/
|
||||||
class WCS_Upgrade_1_2 {
|
class WCS_Upgrade_1_2 {
|
||||||
|
|
||||||
public static function init() {
|
public static function init() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
|
|
||||||
// Get IDs only and use a direct DB query for efficiency
|
// Get IDs only and use a direct DB query for efficiency
|
||||||
$orders_to_upgrade = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order' AND post_parent = 0" );
|
$orders_to_upgrade = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order' AND post_parent = 0" );
|
||||||
@@ -70,10 +74,10 @@ class WCS_Upgrade_1_2 {
|
|||||||
$order_discount = $order->get_total_discount();
|
$order_discount = $order->get_total_discount();
|
||||||
update_post_meta( $order_id, '_order_recurring_discount_total', $order_discount );
|
update_post_meta( $order_id, '_order_recurring_discount_total', $order_discount );
|
||||||
|
|
||||||
$order_shipping_tax = get_post_meta( $order_id, '_order_shipping_tax', true );
|
$order_shipping_tax = $order->get_meta( '_order_shipping_tax', true );
|
||||||
update_post_meta( $order_id, '_order_recurring_shipping_tax_total', $order_shipping_tax );
|
update_post_meta( $order_id, '_order_recurring_shipping_tax_total', $order_shipping_tax );
|
||||||
|
|
||||||
$order_tax = get_post_meta( $order_id, '_order_tax', true ); // $order->get_total_tax() includes shipping tax
|
$order_tax = $order->get_meta( '_order_tax', true ); // $order->get_total_tax() includes shipping tax
|
||||||
update_post_meta( $order_id, '_order_recurring_tax_total', $order_tax );
|
update_post_meta( $order_id, '_order_recurring_tax_total', $order_tax );
|
||||||
|
|
||||||
$order_total = $order->get_total();
|
$order_total = $order->get_total();
|
||||||
|
@@ -14,10 +14,13 @@
|
|||||||
if ( ! defined( 'ABSPATH' ) ) {
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @deprecated subscription-core 7.7.0
|
||||||
|
*/
|
||||||
class WCS_Upgrade_1_3 {
|
class WCS_Upgrade_1_3 {
|
||||||
|
|
||||||
public static function init() {
|
public static function init() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
// Change transient timeout entries to be a vanilla option
|
// Change transient timeout entries to be a vanilla option
|
||||||
|
@@ -14,12 +14,15 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
|
*/
|
||||||
class WCS_Upgrade_1_4 {
|
class WCS_Upgrade_1_4 {
|
||||||
|
|
||||||
private static $last_upgraded_user_id = false;
|
private static $last_upgraded_user_id = false;
|
||||||
|
|
||||||
public static function init() {
|
public static function init() {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
$subscriptions_meta_key = $wpdb->get_blog_prefix() . 'woocommerce_subscriptions';
|
$subscriptions_meta_key = $wpdb->get_blog_prefix() . 'woocommerce_subscriptions';
|
||||||
@@ -166,11 +169,11 @@ class WCS_Upgrade_1_4 {
|
|||||||
*
|
*
|
||||||
* Needs to be a separate function so that it can use a static variable (and therefore avoid calling get_option() thousands
|
* Needs to be a separate function so that it can use a static variable (and therefore avoid calling get_option() thousands
|
||||||
* of times when iterating over thousands of users).
|
* of times when iterating over thousands of users).
|
||||||
*
|
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.4
|
||||||
|
* @deprecated subscriptions-core 7.7.0
|
||||||
*/
|
*/
|
||||||
public static function is_user_upgraded( $user_id ) {
|
public static function is_user_upgraded( $user_id ) {
|
||||||
|
wcs_deprecated_function( __METHOD__, 'subscriptions-core 7.7.0' );
|
||||||
if ( false === self::$last_upgraded_user_id ) {
|
if ( false === self::$last_upgraded_user_id ) {
|
||||||
self::$last_upgraded_user_id = get_option( 'wcs_1_4_last_upgraded_user_id', 0 );
|
self::$last_upgraded_user_id = get_option( 'wcs_1_4_last_upgraded_user_id', 0 );
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ class WCS_Upgrade_1_5 {
|
|||||||
* Subscriptions 1.5 made it possible for a product to be sold individually or in multiple quantities, whereas
|
* Subscriptions 1.5 made it possible for a product to be sold individually or in multiple quantities, whereas
|
||||||
* previously it was possible only to buy a subscription product in a single quantity.
|
* previously it was possible only to buy a subscription product in a single quantity.
|
||||||
*
|
*
|
||||||
|
* @deprecated
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
*/
|
*/
|
||||||
public static function upgrade_products() {
|
public static function upgrade_products() {
|
||||||
@@ -54,6 +55,7 @@ class WCS_Upgrade_1_5 {
|
|||||||
/**
|
/**
|
||||||
* Update subscription WP-Cron tasks to Action Scheduler.
|
* Update subscription WP-Cron tasks to Action Scheduler.
|
||||||
*
|
*
|
||||||
|
* @deprecated
|
||||||
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
* @since 1.0.0 - Migrated from WooCommerce Subscriptions v2.0
|
||||||
*/
|
*/
|
||||||
public static function upgrade_hooks( $number_hooks_to_upgrade ) {
|
public static function upgrade_hooks( $number_hooks_to_upgrade ) {
|
||||||
|
@@ -12,6 +12,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Upgrade_2_0 {
|
class WCS_Upgrade_2_0 {
|
||||||
|
|
||||||
/* Cache of order item meta keys that were used to store subscription data in v1.5 */
|
/* Cache of order item meta keys that were used to store subscription data in v1.5 */
|
||||||
@@ -214,12 +217,13 @@ class WCS_Upgrade_2_0 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( ! array_key_exists( $raw_subscription->order_item_id, $subscriptions ) ) {
|
if ( ! array_key_exists( $raw_subscription->order_item_id, $subscriptions ) ) {
|
||||||
|
$order = wc_get_order( $raw_subscription->order_id );
|
||||||
$subscriptions[ $raw_subscription->order_item_id ] = array(
|
$subscriptions[ $raw_subscription->order_item_id ] = array(
|
||||||
'order_id' => $raw_subscription->order_id,
|
'order_id' => $raw_subscription->order_id,
|
||||||
'name' => $raw_subscription->order_item_name,
|
'name' => $raw_subscription->order_item_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
$subscriptions[ $raw_subscription->order_item_id ]['user_id'] = (int) get_post_meta( $raw_subscription->order_id, '_customer_user', true );
|
$subscriptions[ $raw_subscription->order_item_id ]['user_id'] = (int) $order->get_meta( '_customer_user', true );
|
||||||
}
|
}
|
||||||
|
|
||||||
$meta_key = str_replace( '_subscription', '', $raw_subscription->meta_key );
|
$meta_key = str_replace( '_subscription', '', $raw_subscription->meta_key );
|
||||||
@@ -610,8 +614,7 @@ class WCS_Upgrade_2_0 {
|
|||||||
$order_meta = get_post_meta( wcs_get_objects_property( $order, 'id' ) );
|
$order_meta = get_post_meta( wcs_get_objects_property( $order, 'id' ) );
|
||||||
|
|
||||||
foreach ( $post_meta_with_new_key as $subscription_meta_key => $order_meta_key ) {
|
foreach ( $post_meta_with_new_key as $subscription_meta_key => $order_meta_key ) {
|
||||||
|
$order_meta_value = $order->get_meta( $order_meta_key, true );
|
||||||
$order_meta_value = get_post_meta( wcs_get_objects_property( $order, 'id' ), $order_meta_key, true );
|
|
||||||
|
|
||||||
if ( isset( $order_meta[ $order_meta_key ] ) && '' !== $order_meta[ $order_meta_key ] ) {
|
if ( isset( $order_meta[ $order_meta_key ] ) && '' !== $order_meta[ $order_meta_key ] ) {
|
||||||
update_post_meta( $subscription_id, $subscription_meta_key, $order_meta_value );
|
update_post_meta( $subscription_id, $subscription_meta_key, $order_meta_value );
|
||||||
@@ -838,7 +841,7 @@ class WCS_Upgrade_2_0 {
|
|||||||
$new_subscription_id = wcs_get_objects_property( $new_subscription, 'id' );
|
$new_subscription_id = wcs_get_objects_property( $new_subscription, 'id' );
|
||||||
|
|
||||||
// Set the post meta on the new subscription and old order
|
// Set the post meta on the new subscription and old order
|
||||||
foreach ( get_post_meta( $resubscribe_order_id, '_original_order', false ) as $original_order_id ) {
|
foreach ( $resubscribe_order->get_meta( '_original_order', false ) as $original_order_id ) {
|
||||||
|
|
||||||
// Because self::get_subscriptions() orders by order ID, it's safe to use wcs_get_subscriptions_for_order() here because the subscription in the new format will have been created for the original order (because its ID will be < the resubscribe order's ID)
|
// Because self::get_subscriptions() orders by order ID, it's safe to use wcs_get_subscriptions_for_order() here because the subscription in the new format will have been created for the original order (because its ID will be < the resubscribe order's ID)
|
||||||
foreach ( wcs_get_subscriptions_for_order( $original_order_id ) as $old_subscription ) {
|
foreach ( wcs_get_subscriptions_for_order( $original_order_id ) as $old_subscription ) {
|
||||||
@@ -876,7 +879,7 @@ class WCS_Upgrade_2_0 {
|
|||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
// If the order doesn't contain a switch, we don't need to do anything
|
// If the order doesn't contain a switch, we don't need to do anything
|
||||||
if ( '' == get_post_meta( wcs_get_objects_property( $switch_order, 'id' ), '_switched_subscription_key', true ) ) {
|
if ( '' === $switch_order->get_meta( '_switched_subscription_key', true ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,6 +12,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Upgrade_2_1 {
|
class WCS_Upgrade_2_1 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -12,6 +12,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Upgrade_2_2_7 {
|
class WCS_Upgrade_2_2_7 {
|
||||||
|
|
||||||
private static $cron_hook = 'wcs_repair_end_of_prepaid_term_actions';
|
private static $cron_hook = 'wcs_repair_end_of_prepaid_term_actions';
|
||||||
|
@@ -13,6 +13,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Upgrade_2_2_9 {
|
class WCS_Upgrade_2_2_9 {
|
||||||
|
|
||||||
private static $cron_hook = 'wcs_repair_subscriptions_containing_synced_variations';
|
private static $cron_hook = 'wcs_repair_subscriptions_containing_synced_variations';
|
||||||
|
@@ -13,6 +13,9 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
exit; // Exit if accessed directly
|
exit; // Exit if accessed directly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
class WCS_Upgrade_Subscription_Post_Author extends WCS_Background_Upgrader {
|
class WCS_Upgrade_Subscription_Post_Author extends WCS_Background_Upgrader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -129,12 +129,10 @@ function wcs_get_subscription_id_from_key( $subscription_key ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$order_and_product_id = explode( '_', $subscription_key );
|
$order_and_product_id = explode( '_', $subscription_key );
|
||||||
|
$subscription_ids = array();
|
||||||
$subscription_ids = array();
|
|
||||||
|
|
||||||
// If we have an order ID and product ID, query based on that
|
// If we have an order ID and product ID, query based on that
|
||||||
if ( ! empty( $order_and_product_id[0] ) && ! empty( $order_and_product_id[1] ) ) {
|
if ( ! empty( $order_and_product_id[0] ) && ! empty( $order_and_product_id[1] ) ) {
|
||||||
|
|
||||||
$subscription_ids = $wpdb->get_col( $wpdb->prepare( "
|
$subscription_ids = $wpdb->get_col( $wpdb->prepare( "
|
||||||
SELECT DISTINCT order_items.order_id FROM {$wpdb->prefix}woocommerce_order_items as order_items
|
SELECT DISTINCT order_items.order_id FROM {$wpdb->prefix}woocommerce_order_items as order_items
|
||||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS itemmeta ON order_items.order_item_id = itemmeta.order_item_id
|
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS itemmeta ON order_items.order_item_id = itemmeta.order_item_id
|
||||||
@@ -146,15 +144,16 @@ function wcs_get_subscription_id_from_key( $subscription_key ) {
|
|||||||
$order_and_product_id[0], $order_and_product_id[1] ) );
|
$order_and_product_id[0], $order_and_product_id[1] ) );
|
||||||
|
|
||||||
} elseif ( ! empty( $order_and_product_id[0] ) ) {
|
} elseif ( ! empty( $order_and_product_id[0] ) ) {
|
||||||
|
// Not replacing this `get_posts` call with `wc_get_orders`. This is from when subscription object used to be an array of data stored in order meta ({$order_id}_{$product_id}, referred to as a "subscriptions key")
|
||||||
$subscription_ids = get_posts( array(
|
$subscription_ids = get_posts(
|
||||||
'posts_per_page' => 1,
|
array(
|
||||||
'post_parent' => $order_and_product_id[0],
|
'posts_per_page' => 1,
|
||||||
'post_status' => 'any',
|
'post_parent' => $order_and_product_id[0],
|
||||||
'post_type' => 'shop_subscription',
|
'post_status' => 'any',
|
||||||
'fields' => 'ids',
|
'post_type' => 'shop_subscription',
|
||||||
) );
|
'fields' => 'ids',
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ( ! empty( $subscription_ids ) ) ? $subscription_ids[0] : null;
|
return ( ! empty( $subscription_ids ) ) ? $subscription_ids[0] : null;
|
||||||
|
@@ -60,13 +60,13 @@ function wcs_is_product_limited_for_user( $product, $user_id = 0 ) {
|
|||||||
// If the product is limited for any status, there exists a chance that the customer has cancelled subscriptions which cannot be resubscribed to as they have no completed payments.
|
// If the product is limited for any status, there exists a chance that the customer has cancelled subscriptions which cannot be resubscribed to as they have no completed payments.
|
||||||
if ( 'any' === $product_limitation && $is_limited_for_user ) {
|
if ( 'any' === $product_limitation && $is_limited_for_user ) {
|
||||||
$is_limited_for_user = false;
|
$is_limited_for_user = false;
|
||||||
$user_subscriptions = wcs_get_subscriptions( array(
|
|
||||||
'subscriptions_per_page' => -1,
|
|
||||||
'customer_id' => $user_id,
|
|
||||||
'product_id' => $product->get_id(),
|
|
||||||
) );
|
|
||||||
|
|
||||||
foreach ( $user_subscriptions as $subscription ) {
|
foreach ( wcs_get_users_subscriptions( $user_id ) as $subscription ) {
|
||||||
|
// Skip if the subscription is not for the product we are checking.
|
||||||
|
if ( ! $subscription->has_product( $product->get_id() ) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! $subscription->has_status( 'cancelled' ) || 0 !== $subscription->get_payment_count() ) {
|
if ( ! $subscription->has_status( 'cancelled' ) || 0 !== $subscription->get_payment_count() ) {
|
||||||
$is_limited_for_user = true;
|
$is_limited_for_user = true;
|
||||||
break;
|
break;
|
||||||
|
@@ -108,11 +108,14 @@ function wcs_cart_contains_failed_renewal_order_payment() {
|
|||||||
$cart_item = wcs_cart_contains_renewal();
|
$cart_item = wcs_cart_contains_renewal();
|
||||||
|
|
||||||
if ( false !== $cart_item && isset( $cart_item['subscription_renewal']['renewal_order_id'] ) ) {
|
if ( false !== $cart_item && isset( $cart_item['subscription_renewal']['renewal_order_id'] ) ) {
|
||||||
$renewal_order = wc_get_order( $cart_item['subscription_renewal']['renewal_order_id'] );
|
$renewal_order = wc_get_order( $cart_item['subscription_renewal']['renewal_order_id'] );
|
||||||
$is_failed_renewal_order = apply_filters( 'woocommerce_subscriptions_is_failed_renewal_order', $renewal_order->has_status( 'failed' ), $cart_item['subscription_renewal']['renewal_order_id'], $renewal_order->get_status() );
|
|
||||||
|
|
||||||
if ( $is_failed_renewal_order ) {
|
if ( $renewal_order ) {
|
||||||
$contains_renewal = $cart_item;
|
$is_failed_renewal_order = apply_filters( 'woocommerce_subscriptions_is_failed_renewal_order', $renewal_order->has_status( 'failed' ), $cart_item['subscription_renewal']['renewal_order_id'], $renewal_order->get_status() );
|
||||||
|
|
||||||
|
if ( $is_failed_renewal_order ) {
|
||||||
|
$contains_renewal = $cart_item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,7 +29,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
// translators: placeholder is a currency symbol / code
|
// translators: placeholder is a currency symbol / code
|
||||||
'label' => sprintf( __( 'Subscription Price (%s)', 'woocommerce-subscriptions' ), get_woocommerce_currency_symbol() ),
|
'label' => sprintf( __( 'Subscription Price (%s)', 'woocommerce-subscriptions' ), get_woocommerce_currency_symbol() ),
|
||||||
'placeholder' => _x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ),
|
'placeholder' => _x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ),
|
||||||
'value' => get_post_meta( $variation->get_id(), '_subscription_price', true ),
|
'value' => $variation->get_meta( '_subscription_price', true ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'custom_attributes' => array(
|
'custom_attributes' => array(
|
||||||
'step' => 'any',
|
'step' => 'any',
|
||||||
@@ -45,7 +45,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
'wrapper_class' => '_subscription_period_interval_field',
|
'wrapper_class' => '_subscription_period_interval_field',
|
||||||
'label' => __( 'Subscription Periods', 'woocommerce-subscriptions' ),
|
'label' => __( 'Subscription Periods', 'woocommerce-subscriptions' ),
|
||||||
'options' => wcs_get_subscription_period_interval_strings(),
|
'options' => wcs_get_subscription_period_interval_strings(),
|
||||||
'value' => get_post_meta( $variation->get_id(), '_subscription_period_interval', true ),
|
'value' => $variation->get_meta( '_subscription_period_interval', true ),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
'wrapper_class' => '_subscription_length_field',
|
'wrapper_class' => '_subscription_length_field',
|
||||||
'label' => __( 'Subscription Length', 'woocommerce-subscriptions' ),
|
'label' => __( 'Subscription Length', 'woocommerce-subscriptions' ),
|
||||||
'options' => wcs_get_subscription_ranges( $subscription_period ),
|
'options' => wcs_get_subscription_ranges( $subscription_period ),
|
||||||
'value' => get_post_meta( $variation->get_id(), '_subscription_length', true ),
|
'value' => $variation->get_meta( '_subscription_length', true ),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
?>
|
?>
|
||||||
@@ -84,7 +84,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
'wrapper_class' => '_subscription_sign_up_fee_field',
|
'wrapper_class' => '_subscription_sign_up_fee_field',
|
||||||
'label' => sprintf( __( 'Sign-up Fee (%s)', 'woocommerce-subscriptions' ), get_woocommerce_currency_symbol() ),
|
'label' => sprintf( __( 'Sign-up Fee (%s)', 'woocommerce-subscriptions' ), get_woocommerce_currency_symbol() ),
|
||||||
'placeholder' => _x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ),
|
'placeholder' => _x( 'e.g. 9.90', 'example price', 'woocommerce-subscriptions' ),
|
||||||
'value' => get_post_meta( $variation->get_id(), '_subscription_sign_up_fee', true ),
|
'value' => $variation->get_meta( '_subscription_sign_up_fee', true ),
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'custom_attributes' => array(
|
'custom_attributes' => array(
|
||||||
'step' => 'any',
|
'step' => 'any',
|
||||||
@@ -103,7 +103,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
'wrapper_class' => '_subscription_trial_length_field',
|
'wrapper_class' => '_subscription_trial_length_field',
|
||||||
'label' => __( 'Free Trial', 'woocommerce-subscriptions' ),
|
'label' => __( 'Free Trial', 'woocommerce-subscriptions' ),
|
||||||
'placeholder' => _x( 'e.g. 3', 'example number of days / weeks / months', 'woocommerce-subscriptions' ),
|
'placeholder' => _x( 'e.g. 3', 'example number of days / weeks / months', 'woocommerce-subscriptions' ),
|
||||||
'value' => get_post_meta( $variation->get_id(), '_subscription_trial_length', true ),
|
'value' => $variation->get_meta( '_subscription_trial_length', true ),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -24,6 +24,6 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ( $notice->is_dismissible() ) : ?>
|
<?php if ( $notice->is_dismissible() ) : ?>
|
||||||
<a href="<?php echo esc_url( $notice->print_dismiss_url() ); ?>" type="button" class="notice-dismiss" style="text-decoration: none;"></a>
|
<a href="<?php $notice->print_dismiss_url(); ?>" type="button" class="notice-dismiss" style="text-decoration: none;"></a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
89
vendor/woocommerce/subscriptions-core/templates/emails/customer-notification-auto-renewal.php
vendored
Normal file
89
vendor/woocommerce/subscriptions-core/templates/emails/customer-notification-auto-renewal.php
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Notify the customer that an automated renewal their subscription is about to happen.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_header() Output the email header.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Hi %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo wp_kses(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your subscription will <strong>automatically renew</strong> in %1$s — that’s <strong>%2$s</strong>.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
),
|
||||||
|
[ 'strong' => [] ]
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
esc_html_e( 'Here are the details:', 'woocommerce-subscriptions' );
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text, true );
|
||||||
|
?>
|
||||||
|
<p>
|
||||||
|
<small>
|
||||||
|
<?php
|
||||||
|
echo wp_kses_post(
|
||||||
|
sprintf(
|
||||||
|
// translators: %s: link to subscription detail in the customer's dashboard.
|
||||||
|
__( 'You can manage this subscription from your %s', 'woocommerce-subscriptions' ),
|
||||||
|
'<a href="' . esc_url( $subscription->get_view_order_url() ) . '">' . esc_html__( 'account dashboard', 'woocommerce-subscriptions' ) . '</a>',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</small>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo wp_kses_post( wpautop( wptexturize( $additional_content ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_footer() Output the email footer.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_footer', $email );
|
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Free trial of an automatically renewed subscription is about to expire email.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_header() Output the email header.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Hi, %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo wp_kses(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your paid subscription begins when your free trial expires in %1$s — that’s <strong>%2$s</strong>.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
),
|
||||||
|
[ 'strong' => [] ]
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo wp_kses(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: link to account dashboard.
|
||||||
|
__( 'Payment will be deducted using the payment method on file. You can manage this subscription from your %1$s.', 'woocommerce-subscriptions' ),
|
||||||
|
'<a href="' . esc_url( $subscription->get_view_order_url() ) . '">' . esc_html__( 'account dashboard', 'woocommerce-subscriptions' ) . '</a>'
|
||||||
|
),
|
||||||
|
[ 'a' => [ 'href' => true ] ]
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
esc_html_e( 'Here are the details:', 'woocommerce-subscriptions' );
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text, true );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo wp_kses_post( wpautop( wptexturize( $additional_content ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_footer() Output the email footer.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_footer', $email );
|
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Subscription is about to expire email.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_header() Output the email header.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Heads up, %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo wp_kses(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your subscription expires in %1$s — that’s <strong>%2$s</strong>.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
),
|
||||||
|
[ 'strong' => [] ]
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
esc_html_e( 'Here are the details:', 'woocommerce-subscriptions' );
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text, true );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo wp_kses_post( wpautop( wptexturize( $additional_content ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_footer() Output the email footer.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_footer', $email );
|
114
vendor/woocommerce/subscriptions-core/templates/emails/customer-notification-manual-renewal.php
vendored
Normal file
114
vendor/woocommerce/subscriptions-core/templates/emails/customer-notification-manual-renewal.php
vendored
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Manual renewal needed.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_header() Output the email header.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Hi %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo wp_kses(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your subscription is up for renewal in %1$s — that’s <strong>%2$s</strong>.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
),
|
||||||
|
[ 'strong' => [] ]
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>
|
||||||
|
<?php
|
||||||
|
esc_html_e( 'This subscription will not renew automatically.', 'woocommerce-subscriptions' );
|
||||||
|
?>
|
||||||
|
</strong>
|
||||||
|
<?php
|
||||||
|
if ( $can_renew_early ) {
|
||||||
|
echo wp_kses(
|
||||||
|
__( 'You can <strong>renew it manually</strong> in a few short steps via the <em>Subscriptions</em> tab in your account dashboard.', 'woocommerce-subscriptions' ),
|
||||||
|
[
|
||||||
|
'strong' => [],
|
||||||
|
'em' => [],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<table role="presentation" border="0" cellspacing="0" cellpadding="0" style="margin: 0 auto;">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
if ( $can_renew_early ) {
|
||||||
|
$link_text = __( 'Renew Subscription', 'woocommerce-subscriptions' );
|
||||||
|
} else {
|
||||||
|
$link_text = __( 'Manage Subscription', 'woocommerce-subscriptions' );
|
||||||
|
}
|
||||||
|
echo wp_kses(
|
||||||
|
'<a href="' . esc_url( $url_for_renewal ) . '">' . esc_html( $link_text ) . '</a>',
|
||||||
|
[ 'a' => [ 'href' => true ] ]
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
esc_html_e( 'Here are the details:', 'woocommerce-subscriptions' );
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo wp_kses_post( wpautop( wptexturize( $additional_content ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_footer() Output the email footer.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_footer', $email );
|
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Free trial of a manually renewed subscription is about to expire email.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_header() Output the email header.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Heads up, %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
echo wp_kses(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your free trial expires in %1$s — that’s <strong>%2$s</strong>.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
),
|
||||||
|
[ 'strong' => [] ]
|
||||||
|
);
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo wp_kses_post( wpautop( wptexturize( $additional_content ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @hooked WC_Emails::email_footer() Output the email footer.
|
||||||
|
*
|
||||||
|
* @since x.x.x
|
||||||
|
*/
|
||||||
|
do_action( 'woocommerce_email_footer', $email );
|
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Notify the customer that an automated renewal their subscription is about to happen. Plain text version.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails/Plain
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( $email_heading . "\n\n" );
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Hi %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your subscription will automatically renew in %1$s — that’s %2$s.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
esc_html_e( 'Here are the details:', 'woocommerce-subscriptions' );
|
||||||
|
echo "\n";
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text, true );
|
||||||
|
|
||||||
|
esc_html_e( 'You can manage this subscription from your account dashboard: ', 'woocommerce-subscriptions' );
|
||||||
|
echo esc_url( wc_get_page_permalink( 'myaccount' ) );
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
|
@@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Free trial of an automatically renewed subscription is about to expire email. Plain text version.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails/Plain
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( $email_heading . "\n\n" );
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Hi, %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your paid subscription begins when your free trial expires in %1$s — that’s %2$s.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
// translators: %1$s: link to account dashboard.
|
||||||
|
esc_html_e( 'Payment will be deducted using the payment method on file. You can manage this subscription from your account dashboard: ', 'woocommerce-subscriptions' );
|
||||||
|
echo esc_url( wc_get_page_permalink( 'myaccount' ) );
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
esc_html_e( 'Here are the details:', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text, true );
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
|
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Subscription is about to expire email. Plain text version.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails/Plain
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( $email_heading . "\n\n" );
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Heads up, %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your subscription expires in %1$s — that’s %2$s.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
esc_html_e( 'Here are the details:', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text, true );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
|
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Manual renewal needed. Plain text version.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails/Plain
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( $email_heading . "\n" );
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Hi %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your subscription is up for renewal in %1$s — that’s %2$s.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
esc_html_e( 'This subscription will not renew automatically.', 'woocommerce-subscriptions' );
|
||||||
|
echo "\n";
|
||||||
|
if ( $can_renew_early ) {
|
||||||
|
esc_html_e(
|
||||||
|
'You can renew it manually in a few short steps via the Subscriptions tab in your account dashboard.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
if ( $can_renew_early ) {
|
||||||
|
esc_html_e( 'Renew my subscription: ', 'woocommerce-subscriptions' );
|
||||||
|
echo esc_url( $url_for_renewal );
|
||||||
|
} else {
|
||||||
|
esc_html_e( 'Manage my subscription: ', 'woocommerce-subscriptions' );
|
||||||
|
echo esc_url( $url_for_renewal );
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
esc_html_e( 'Here are the details:', 'woocommerce-subscriptions' );
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text );
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
|
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Customer Notification: Free trial of a manually renewed subscription is about to expire email. Plain text version.
|
||||||
|
*
|
||||||
|
* @package WooCommerce_Subscriptions/Templates/Emails/Plain
|
||||||
|
* @version x.x.x
|
||||||
|
*/
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( $email_heading . "\n\n" );
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
/* translators: %s: Customer first name */
|
||||||
|
__( 'Heads up, %s.', 'woocommerce-subscriptions' ),
|
||||||
|
$subscription->get_billing_first_name()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n";
|
||||||
|
|
||||||
|
echo esc_html(
|
||||||
|
sprintf(
|
||||||
|
// translators: %1$s: human readable time difference (eg 3 days, 1 day), %2$s: date in local format.
|
||||||
|
__(
|
||||||
|
'Your free trial expires in %1$s — that’s %2$s.',
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$subscription_time_til_event,
|
||||||
|
$subscription_event_date
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
// Show subscription details.
|
||||||
|
\WC_Subscriptions_Email::subscription_details( $subscription, $order, $sent_to_admin, $plain_text );
|
||||||
|
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show user-defined additional content - this is set in each email's settings.
|
||||||
|
*/
|
||||||
|
if ( $additional_content ) {
|
||||||
|
echo esc_html( wp_strip_all_tags( wptexturize( $additional_content ) ) );
|
||||||
|
echo "\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo esc_html( apply_filters( 'woocommerce_email_footer_text', get_option( 'woocommerce_email_footer_text' ) ) );
|
@@ -40,7 +40,7 @@ foreach ( $subscriptions as $subscription ) {
|
|||||||
|
|
||||||
echo "\n\n";
|
echo "\n\n";
|
||||||
}
|
}
|
||||||
if ( $has_automatic_renewal && ! $is_admin_email && $subscription->get_time( 'next_payment' ) > 0 ) {
|
if ( $has_automatic_renewal && ! $is_admin_email && $subscription->get_time( 'next_payment' ) > 0 && ! $skip_my_account_link ) {
|
||||||
if ( count( $subscriptions ) === 1 ) {
|
if ( count( $subscriptions ) === 1 ) {
|
||||||
$subscription = reset( $subscriptions );
|
$subscription = reset( $subscriptions );
|
||||||
$my_account_url = $subscription->get_view_order_url();
|
$my_account_url = $subscription->get_view_order_url();
|
||||||
@@ -49,10 +49,15 @@ if ( $has_automatic_renewal && ! $is_admin_email && $subscription->get_time( 'ne
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Translators: Placeholder is the My Account URL.
|
// Translators: Placeholder is the My Account URL.
|
||||||
echo wp_kses_post( sprintf( _n(
|
echo wp_kses_post(
|
||||||
'This subscription is set to renew automatically using your payment method on file. You can manage or cancel this subscription from your my account page. %s',
|
sprintf(
|
||||||
'These subscriptions are set to renew automatically using your payment method on file. You can manage or cancel your subscriptions from your my account page. %s',
|
_n(
|
||||||
count( $subscriptions ),
|
'This subscription is set to renew automatically using your payment method on file. You can manage or cancel this subscription from your my account page. %s',
|
||||||
'woocommerce-subscriptions'
|
'These subscriptions are set to renew automatically using your payment method on file. You can manage or cancel your subscriptions from your my account page. %s',
|
||||||
), $my_account_url ) );
|
count( $subscriptions ),
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
$my_account_url
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,8 @@ $is_parent_order = wcs_order_contains_subscription( $order, 'parent' );
|
|||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<?php if ( $has_automatic_renewal && ! $is_admin_email && $subscription->get_time( 'next_payment' ) > 0 ) {
|
<?php
|
||||||
|
if ( $has_automatic_renewal && ! $is_admin_email && $subscription->get_time( 'next_payment' ) > 0 && ! $skip_my_account_link ) {
|
||||||
if ( count( $subscriptions ) === 1 ) {
|
if ( count( $subscriptions ) === 1 ) {
|
||||||
$subscription = reset( $subscriptions );
|
$subscription = reset( $subscriptions );
|
||||||
$my_account_url = $subscription->get_view_order_url();
|
$my_account_url = $subscription->get_view_order_url();
|
||||||
@@ -55,12 +56,22 @@ $is_parent_order = wcs_order_contains_subscription( $order, 'parent' );
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Translators: Placeholders are opening and closing My Account link tags.
|
// Translators: Placeholders are opening and closing My Account link tags.
|
||||||
printf( '<small>%s</small>', wp_kses_post( sprintf( _n(
|
printf(
|
||||||
'This subscription is set to renew automatically using your payment method on file. You can manage or cancel this subscription from your %smy account page%s.',
|
'<small>%s</small>',
|
||||||
'These subscriptions are set to renew automatically using your payment method on file. You can manage or cancel your subscriptions from your %smy account page%s.',
|
wp_kses_post(
|
||||||
count( $subscriptions ),
|
sprintf(
|
||||||
'woocommerce-subscriptions'
|
_n(
|
||||||
), '<a href="' . $my_account_url . '">', '</a>' ) ) );
|
'This subscription is set to renew automatically using your payment method on file. You can manage or cancel this subscription from your %1$smy account page%2$s.',
|
||||||
}?>
|
'These subscriptions are set to renew automatically using your payment method on file. You can manage or cancel your subscriptions from your %1$smy account page%2$s.',
|
||||||
|
count( $subscriptions ),
|
||||||
|
'woocommerce-subscriptions'
|
||||||
|
),
|
||||||
|
'<a href="' . $my_account_url . '">',
|
||||||
|
'</a>'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -76,7 +76,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
$purchase_note = get_post_meta( $_product->get_id(), '_purchase_note', true );
|
$purchase_note = $_product->get_purchase_note();
|
||||||
if ( $subscription->has_status( array( 'completed', 'processing' ) ) && $purchase_note ) {
|
if ( $subscription->has_status( array( 'completed', 'processing' ) ) && $purchase_note ) {
|
||||||
?>
|
?>
|
||||||
<tr class="product-purchase-note">
|
<tr class="product-purchase-note">
|
||||||
|
@@ -458,7 +458,7 @@ function wcs_get_subscriptions( $args ) {
|
|||||||
'status' => $args['subscription_status'],
|
'status' => $args['subscription_status'],
|
||||||
'limit' => $args['subscriptions_per_page'],
|
'limit' => $args['subscriptions_per_page'],
|
||||||
'page' => $args['paged'],
|
'page' => $args['paged'],
|
||||||
'offset' => $args['offset'],
|
'offset' => $args['offset'] > 0 ? $args['offset'] : null,
|
||||||
'order' => $args['order'],
|
'order' => $args['order'],
|
||||||
'return' => 'ids',
|
'return' => 'ids',
|
||||||
// just in case we need to filter or order by meta values later
|
// just in case we need to filter or order by meta values later
|
||||||
@@ -509,10 +509,23 @@ function wcs_get_subscriptions( $args ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to restrict subscriptions to those which contain a certain product/variation
|
// It's more efficient to filter the results by product ID or variation ID rather than querying for via a "post__in" clause.
|
||||||
if ( ( 0 !== $args['product_id'] && is_numeric( $args['product_id'] ) ) || ( 0 !== $args['variation_id'] && is_numeric( $args['variation_id'] ) ) ) {
|
// This can only work where we know that the results will be sufficiently limited by the other query args. ie when we're querying by customer_id or order_id.
|
||||||
$subscriptions_for_product = wcs_get_subscriptions_for_product( array( $args['product_id'], $args['variation_id'] ) );
|
// We store the filters in a separate array so that we can apply them after the query has been run.
|
||||||
$query_args = WCS_Admin_Post_Types::set_post__in_query_var( $query_args, $subscriptions_for_product );
|
$query_controller = new WC_Subscription_Query_Controller( $args );
|
||||||
|
|
||||||
|
// We need to restrict subscriptions to those which contain a certain product/variation.
|
||||||
|
if ( $query_controller->has_product_query() ) {
|
||||||
|
if ( $query_controller->should_filter_query_results() ) {
|
||||||
|
// We will filter the results and apply any paging, limit and offset after the query has been run.
|
||||||
|
unset( $args['product_id'], $args['variation_id'], $query_args['limit'], $query_args['paged'], $query_args['offset'] );
|
||||||
|
|
||||||
|
// We need to get all subscriptions otherwise the limit could be filled with subscriptions that don't contain the product.
|
||||||
|
$query_args['limit'] = -1;
|
||||||
|
} else {
|
||||||
|
$subscriptions_for_product = wcs_get_subscriptions_for_product( array( $args['product_id'], $args['variation_id'] ) );
|
||||||
|
$query_args = WCS_Admin_Post_Types::set_post__in_query_var( $query_args, $subscriptions_for_product );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! empty( $query_args['meta_query'] ) ) {
|
if ( ! empty( $query_args['meta_query'] ) ) {
|
||||||
@@ -532,6 +545,12 @@ function wcs_get_subscriptions( $args ) {
|
|||||||
$subscriptions[ $subscription_id ] = wcs_get_subscription( $subscription_id );
|
$subscriptions[ $subscription_id ] = wcs_get_subscription( $subscription_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we didn't query the database for subscriptions to a product, filter the results now.
|
||||||
|
if ( $query_controller->has_product_query() && $query_controller->should_filter_query_results() ) {
|
||||||
|
$subscriptions = $query_controller->filter_subscriptions( $subscriptions );
|
||||||
|
$subscriptions = $query_controller->paginate_results( $subscriptions );
|
||||||
|
}
|
||||||
|
|
||||||
return apply_filters( 'woocommerce_got_subscriptions', $subscriptions, $args );
|
return apply_filters( 'woocommerce_got_subscriptions', $subscriptions, $args );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,5 +6,5 @@
|
|||||||
* Author: Automattic
|
* Author: Automattic
|
||||||
* Author URI: https://woocommerce.com/
|
* Author URI: https://woocommerce.com/
|
||||||
* Requires WP: 5.6
|
* Requires WP: 5.6
|
||||||
* Version: 7.6.0
|
* Version: 7.7.1
|
||||||
*/
|
*/
|
||||||
|
@@ -5,11 +5,11 @@
|
|||||||
* Description: Sell products and services with recurring payments in your WooCommerce Store.
|
* Description: Sell products and services with recurring payments in your WooCommerce Store.
|
||||||
* Author: WooCommerce
|
* Author: WooCommerce
|
||||||
* Author URI: https://woocommerce.com/
|
* Author URI: https://woocommerce.com/
|
||||||
* Version: 6.8.0
|
* Version: 6.9.0
|
||||||
* Requires Plugins: woocommerce
|
* Requires Plugins: woocommerce
|
||||||
*
|
*
|
||||||
* WC requires at least: 8.7.1
|
* WC requires at least: 8.7.1
|
||||||
* WC tested up to: 9.3.0
|
* WC tested up to: 9.4
|
||||||
* Woo: 27147:6115e6d7e297b623a169fdcf5728b224
|
* Woo: 27147:6115e6d7e297b623a169fdcf5728b224
|
||||||
*
|
*
|
||||||
* Copyright 2019 WooCommerce
|
* Copyright 2019 WooCommerce
|
||||||
@@ -78,7 +78,7 @@ class WC_Subscriptions {
|
|||||||
public static $plugin_file = __FILE__;
|
public static $plugin_file = __FILE__;
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
public static $version = '6.8.0'; // WRCS: DEFINED_VERSION.
|
public static $version = '6.9.0'; // WRCS: DEFINED_VERSION.
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
public static $wc_minimum_supported_version = '7.7';
|
public static $wc_minimum_supported_version = '7.7';
|
||||||
|
Reference in New Issue
Block a user