plugin updates

This commit is contained in:
Tony Volpe
2024-06-17 14:48:11 -04:00
parent ecc5fbf831
commit 3751a5a1a6
1318 changed files with 91130 additions and 52250 deletions

View File

@@ -264,7 +264,7 @@ class WC_Admin_Marketplace_Promotions {
&& $promotion['menu_item_id'] === $menu_item['id']
) {
$bubble_text = $promotion['content'][ self::$locale ] ?? ( $promotion['content']['en_US'] ?? __( 'Sale', 'woocommerce' ) );
$menu_items[ $index ]['title'] = $menu_item['title'] . self::append_bubble( $bubble_text );
$menu_items[ $index ]['title'] = self::append_bubble( $menu_item['title'], $bubble_text );
break;
}
@@ -274,26 +274,21 @@ class WC_Admin_Marketplace_Promotions {
}
/**
* Return the markup for a menu item bubble with a given text and optional additional attributes.
* Return the markup for a menu item bubble with a given text.
*
* @param string $bubble_text Text of bubble.
* @param array $attributes Optional. Additional attributes for the bubble, such as class or style.
* @param string $menu_item_text Text of menu item we want to change.
* @param string $bubble_text Text of bubble.
*
* @return string
*/
private static function append_bubble( $bubble_text, $attributes = array() ) {
$default_attributes = array(
'class' => 'awaiting-mod update-plugins remaining-tasks-badge woocommerce-task-list-remaining-tasks-badge',
'style' => '',
);
private static function append_bubble( string $menu_item_text, string $bubble_text ): string {
// Strip out update count bubble added by Marketplace::get_marketplace_update_count_html.
$menu_item_text = preg_replace( '|<span class="update-plugins count-[\d]+">[A-z0-9 <>="-]+</span>|', '', $menu_item_text );
$attributes = wp_parse_args( $attributes, $default_attributes );
$class_attr = ! empty( $attributes['class'] ) ? sprintf( 'class="%s"', esc_attr( $attributes['class'] ) ) : '';
$style_attr = ! empty( $attributes['style'] ) ? sprintf( 'style="%s"', esc_attr( $attributes['style'] ) ) : '';
$bubble_html = sprintf( ' <span %s %s>%s</span>', $class_attr, $style_attr, esc_html( $bubble_text ) );
return $bubble_html;
return $menu_item_text
. '<span class="awaiting-mod update-plugins remaining-tasks-badge woocommerce-task-list-remaining-tasks-badge">'
. esc_html( $bubble_text )
. '</span>';
}
/**

View File

@@ -10,6 +10,7 @@ use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
use Automattic\WooCommerce\Internal\Utilities\Users;
use Automattic\WooCommerce\Internal\Utilities\WebhookUtil;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
defined( 'ABSPATH' ) || exit;
@@ -217,7 +218,7 @@ class WC_Admin_Notices {
* or if the Legacy REST API extension is installed, and remove the notice about Legacy webhooks
* if no such webhooks exist anymore or if the Legacy REST API extension is installed.
*
* TODO: Change this method in WooCommerce 9.0 so that the notice gets removed if the Legacy REST API extension is installed and active.
* TODO: Change this method in WooCommerce 9.0 so that the notice get removed if the Legacy REST API extension is installed and active.
*/
private static function maybe_remove_legacy_api_removal_notice() {
$plugin_is_active = is_plugin_active( 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php' );
@@ -229,6 +230,11 @@ class WC_Admin_Notices {
if ( self::has_notice( 'legacy_webhooks_unsupported_in_woo_90' ) && ( $plugin_is_active || 0 === wc_get_container()->get( WebhookUtil::class )->get_legacy_webhooks_count() ) ) {
self::remove_notice( 'legacy_webhooks_unsupported_in_woo_90' );
}
if ( self::has_notice( 'legacy_rest_api_is_incompatible_with_hpos' ) &&
! ( 'yes' === get_option( 'woocommerce_api_enabled' ) && 'yes' === get_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION ) ) ) {
self::remove_notice( 'legacy_rest_api_is_incompatible_with_hpos' );
}
}
// phpcs:enable Generic.Commenting.Todo.TaskFound

View File

@@ -57,6 +57,9 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
$settings[] = include __DIR__ . '/settings/class-wc-settings-accounts.php';
$settings[] = include __DIR__ . '/settings/class-wc-settings-emails.php';
$settings[] = include __DIR__ . '/settings/class-wc-settings-integrations.php';
if ( \Automattic\WooCommerce\Admin\Features\Features::is_enabled( 'launch-your-store' ) ) {
$settings[] = include __DIR__ . '/settings/class-wc-settings-site-visibility.php';
}
$settings[] = include __DIR__ . '/settings/class-wc-settings-advanced.php';
self::$settings = apply_filters( 'woocommerce_get_settings_pages', $settings );

View File

@@ -50,6 +50,8 @@ class WC_Helper_Admin {
$installed_products
);
$woo_connect_notice_type = WC_Helper_Updater::get_woo_connect_notice_type();
$settings['wccomHelper'] = array(
'isConnected' => WC_Helper::is_site_connected(),
'connectURL' => self::get_connection_url(),
@@ -63,6 +65,7 @@ class WC_Helper_Admin {
'wooUpdateManagerInstallUrl' => WC_Woo_Update_Manager_Plugin::generate_install_url(),
'wooUpdateManagerPluginSlug' => WC_Woo_Update_Manager_Plugin::WOO_UPDATE_MANAGER_SLUG,
'wooUpdateCount' => WC_Helper_Updater::get_updates_count_based_on_site_status(),
'woocomConnectNoticeType' => $woo_connect_notice_type,
);
return $settings;

View File

@@ -33,7 +33,7 @@ class WC_Helper_Updater {
* Add the hook for modifying default WPCore update notices on the plugins management page.
*/
public static function add_hook_for_modifying_update_notices() {
if ( ! WC_Woo_Update_Manager_Plugin::is_plugin_active() ) {
if ( ! WC_Woo_Update_Manager_Plugin::is_plugin_active() || ! WC_Helper::is_site_connected() ) {
add_action( 'load-plugins.php', array( __CLASS__, 'setup_update_plugins_messages' ), 11 );
}
}
@@ -155,12 +155,47 @@ class WC_Helper_Updater {
* @return void.
*/
public static function setup_update_plugins_messages() {
$is_site_connected = WC_Helper::is_site_connected();
foreach ( WC_Helper::get_local_woo_plugins() as $plugin ) {
$filename = $plugin['_filename'];
add_action( 'in_plugin_update_message-' . $filename, array( __CLASS__, 'add_install_marketplace_plugin_message' ), 10, 2 );
if ( $is_site_connected ) {
add_action( 'in_plugin_update_message-' . $filename, array( __CLASS__, 'add_install_marketplace_plugin_message' ), 10, 2 );
} else {
add_action( 'in_plugin_update_message-' . $filename, array( __CLASS__, 'add_connect_woocom_plugin_message' ) );
}
}
}
/**
* Runs on in_plugin_update_message-{file-name}, show a message to connect to woocommerce.com for unconnected stores
*
* @return void.
*/
public static function add_connect_woocom_plugin_message() {
$connect_page_url = add_query_arg(
array(
'page' => 'wc-admin',
'tab' => 'my-subscriptions',
'path' => rawurlencode( '/extensions' ),
),
admin_url( 'admin.php' )
);
printf(
wp_kses(
/* translators: 1: Woo Update Manager plugin install URL */
__( ' <a href="%1$s" class="woocommerce-connect-your-store">Connect your store</a> to woocommerce.com to update.', 'woocommerce' ),
array(
'a' => array(
'href' => array(),
'class' => array(),
),
)
),
esc_url( $connect_page_url ),
);
}
/**
* Runs on in_plugin_update_message-{file-name}, show a message to install the Woo Marketplace plugin, on plugin update notification,
* if the Woo Marketplace plugin isn't already installed.
@@ -404,6 +439,9 @@ class WC_Helper_Updater {
* @return array Update data for each requested product.
*/
private static function _update_check( $payload ) {
if ( empty( $payload ) ) {
return array();
}
ksort( $payload );
$hash = md5( wp_json_encode( $payload ) );
@@ -422,13 +460,22 @@ class WC_Helper_Updater {
'errors' => array(),
);
$request = WC_Helper_API::post(
'update-check',
array(
'body' => wp_json_encode( array( 'products' => $payload ) ),
'authenticated' => true,
)
);
if ( WC_Helper::is_site_connected() ) {
$request = WC_Helper_API::post(
'update-check',
array(
'body' => wp_json_encode( array( 'products' => $payload ) ),
'authenticated' => true,
)
);
} else {
$request = WC_Helper_API::post(
'update-check-public',
array(
'body' => wp_json_encode( array( 'products' => $payload ) ),
)
);
}
if ( wp_remote_retrieve_response_code( $request ) !== 200 ) {
$data['errors'][] = 'http-error';
@@ -503,7 +550,7 @@ class WC_Helper_Updater {
*/
public static function get_updates_count_based_on_site_status() {
if ( ! WC_Helper::is_site_connected() ) {
return 1;
return 0;
}
$count = self::get_updates_count() ?? 0;
@@ -514,6 +561,45 @@ class WC_Helper_Updater {
return $count;
}
/**
* Get the type of woo connect notice to be shown in the WC Settings and Marketplace pages.
* - If a store is connected to woocommerce.com or has no installed woo plugins, return 'none'.
* - If a store has installed woo plugins but no updates, return 'short'.
* - If a store has an installed woo plugin with update, return 'long'.
*
* @return string The notice type, 'none', 'short', or 'long'.
*/
public static function get_woo_connect_notice_type() {
if ( WC_Helper::is_site_connected() ) {
return 'none';
}
$woo_plugins = WC_Helper::get_local_woo_plugins();
if ( empty( $woo_plugins ) ) {
return 'none';
}
$update_data = self::get_update_data();
if ( empty( $update_data ) ) {
return 'short';
}
// Scan local plugins.
foreach ( $woo_plugins as $plugin ) {
if ( empty( $update_data[ $plugin['_product_id'] ] ) ) {
continue;
}
if ( version_compare( $plugin['Version'], $update_data[ $plugin['_product_id'] ]['version'], '<' ) ) {
return 'long';
}
}
return 'short';
}
/**
* Return the updates count markup.
*

View File

@@ -5,6 +5,8 @@
* @package WooCommerce\Admin
*/
use Automattic\WooCommerce\Admin\Features\Features;
defined( 'ABSPATH' ) || exit;
/**

View File

@@ -217,11 +217,6 @@ class WC_Settings_General extends WC_Settings_Page {
'id' => 'general_options',
),
array(
'id' => 'wc_settings_general_site_visibility_slotfill',
'type' => 'slotfill_placeholder',
),
array(
'title' => __( 'Currency options', 'woocommerce' ),
'type' => 'title',

View File

@@ -0,0 +1,52 @@
<?php
/**
* WooCommerce site visibility settings
*
* @package WooCommerce\Admin
*/
defined( 'ABSPATH' ) || exit;
/**
* Settings for API.
*/
if ( class_exists( 'WC_Settings_Site_Visibility', false ) ) {
return new WC_Settings_Site_Visibility();
}
/**
* WC_Settings_Advanced.
*/
class WC_Settings_Site_Visibility extends WC_Settings_Page {
/**
* Constructor.
*/
public function __construct() {
$this->id = 'site-visibility';
$this->label = __( 'Site visibility', 'woocommerce' );
parent::__construct();
}
/**
* Get settings for the default section.
*
* @return array
*/
protected function get_settings_for_default_section() {
$settings =
array(
array(
'id' => 'wc_settings_site_visibility_slotfill',
'type' => 'slotfill_placeholder',
),
);
return $settings;
}
}
return new WC_Settings_Site_Visibility();

View File

@@ -741,11 +741,6 @@ if ( 0 < $mu_plugins_count ) :
<td><?php echo $settings['enforce_approved_download_dirs'] ? '<mark class="yes"><span class="dashicons dashicons-yes"></span></mark>' : '<mark class="no">&ndash;</mark>'; ?></td>
</tr>
<tr>
<td data-export-label="HPOS feature screen enabled"><?php esc_html_e( 'HPOS feature screen enabled:', 'woocommerce' ); ?></td>
<td class="help"><?php echo wc_help_tip( esc_html__( 'Is HPOS feature screen enabled?', 'woocommerce' ) ); ?></td>
<td><?php echo $settings['HPOS_feature_screen_enabled'] ? '<mark class="yes"><span class="dashicons dashicons-yes"></span></mark>' : '<mark class="no">&ndash;</mark>'; ?></td>
</tr>
<tr>
<td data-export-label="HPOS feature enabled"><?php esc_html_e( 'HPOS enabled:', 'woocommerce' ); ?></td>
<td class="help"><?php echo wc_help_tip( esc_html__( 'Is HPOS enabled?', 'woocommerce' ) ); ?></td>

View File

@@ -1050,11 +1050,11 @@ class WC_Checkout {
return;
}
// Store Order ID in session so it can be re-used after payment failure.
// Store Order ID in session, so it can be re-used after payment failure.
WC()->session->set( 'order_awaiting_payment', $order_id );
// We save the session early because if the payment gateway hangs
// the request will never finish, thus the session data will neved be saved,
// the request will never finish, thus the session data will never be saved,
// and this can lead to duplicate orders if the user submits the order again.
WC()->session->save_data();
@@ -1073,6 +1073,7 @@ class WC_Checkout {
exit;
}
// Using wp_send_json will gracefully handle any problem encoding data.
wp_send_json( $result );
}
}
@@ -1287,6 +1288,7 @@ class WC_Checkout {
* since it could be empty see:
* https://github.com/woocommerce/woocommerce/issues/24631
*/
if ( apply_filters( 'woocommerce_cart_needs_payment', $order->needs_payment(), WC()->cart ) ) {
$this->process_order_payment( $order_id, $posted_data['payment_method'] );
} else {

View File

@@ -922,7 +922,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
* @return bool
*/
public function is_valid_for_product( $product, $values = array() ) {
if ( ! $this->is_type( wc_get_product_coupon_types() ) ) {
if ( ! $this->is_type( wc_get_product_coupon_types() ) || ! is_a( $product, WC_Product::class ) ) {
return apply_filters( 'woocommerce_coupon_is_valid_for_product', false, $product, $this, $values );
}

View File

@@ -333,7 +333,20 @@ class WC_Discounts {
$items_to_apply[] = $item_to_apply;
}
return $items_to_apply;
/**
* Filters the items that a coupon should be applied to.
*
* This filter allows you to modify the items that a coupon will be applied to before the discount calculations take place.
*
* @since 8.8.0
* @param array $items_to_apply The items that the coupon will be applied to.
* @param WC_Coupon $coupon The coupon object.
* @param WC_Discounts $this The discounts instance.
*
* @return array The modified list of items that the coupon should be applied to.
*/
return apply_filters( 'woocommerce_coupon_get_items_to_apply', $items_to_apply, $coupon, $this );
}
/**

View File

@@ -220,7 +220,7 @@ class WC_Emails {
*/
public function init() {
// Include email classes.
include_once dirname( __FILE__ ) . '/emails/class-wc-email.php';
include_once __DIR__ . '/emails/class-wc-email.php';
$this->emails['WC_Email_New_Order'] = include __DIR__ . '/emails/class-wc-email-new-order.php';
$this->emails['WC_Email_Cancelled_Order'] = include __DIR__ . '/emails/class-wc-email-cancelled-order.php';
@@ -610,8 +610,8 @@ class WC_Emails {
$checkout_fields = Package::container()->get( CheckoutFields::class );
$fields = array_merge(
$checkout_fields->get_order_additional_fields_with_values( $order, 'contact', '', 'view' ),
$checkout_fields->get_order_additional_fields_with_values( $order, 'additional', '', 'view' ),
$checkout_fields->get_order_additional_fields_with_values( $order, 'contact', 'other', 'view' ),
$checkout_fields->get_order_additional_fields_with_values( $order, 'order', 'other', 'view' ),
);
if ( ! $fields ) {

View File

@@ -541,7 +541,9 @@ class WC_Frontend_Scripts {
'checkout_url' => WC_AJAX::get_endpoint( 'checkout' ),
'is_checkout' => is_checkout() && empty( $wp->query_vars['order-pay'] ) && ! isset( $wp->query_vars['order-received'] ) ? 1 : 0,
'debug_mode' => Constants::is_true( 'WP_DEBUG' ),
'i18n_checkout_error' => esc_attr__( 'Error processing checkout. Please try again.', 'woocommerce' ),
/* translators: %s: Order history URL on My Account section */
'i18n_checkout_error' => sprintf( esc_attr__( 'There was an error processing your order. Please check for any charges in your payment method and review your <a href="%s">order history</a> before placing the order again.', 'woocommerce' ), esc_url( wc_get_account_endpoint_url( 'orders' ) ) ),
);
break;
case 'wc-address-i18n':

View File

@@ -248,6 +248,13 @@ class WC_Install {
'8.7.0' => array(
'wc_update_870_prevent_listing_of_transient_files_directory',
),
'8.9.0' => array(
'wc_update_890_update_connect_to_woocommerce_note',
'wc_update_890_update_paypal_standard_load_eligibility',
),
'8.9.1' => array(
'wc_update_891_create_plugin_autoinstall_history_option',
),
);
/**
@@ -1183,16 +1190,30 @@ class WC_Install {
return;
}
// Did we previously install this plugin?
// We check both the woocommerce_history_of_autoinstalled_plugins options (introduced in 9.0)
// and the woocommerce_autoinstalled_plugins option (info should still exist here if the plugin has been uninstalled but not manually reinstalled).
$legacy_api_plugin = 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php';
$autoinstalled_plugins = (array) get_site_option( 'woocommerce_history_of_autoinstalled_plugins', array() );
$previously_installed_by_us = isset( $autoinstalled_plugins[ $legacy_api_plugin ] );
if ( ! $previously_installed_by_us ) {
$autoinstalled_plugins = (array) get_site_option( 'woocommerce_autoinstalled_plugins', array() );
$previously_installed_by_us = isset( $autoinstalled_plugins[ $legacy_api_plugin ] );
}
/**
* Filter to skip the automatic installation of the WooCommerce Legacy REST API plugin
* from the WordPress.org plugins directory.
*
* By default, this is true (skip installation) if we have a record of previously installing the legacy plugin,
* and false (do not skip) if we have no record of previously installing the plugin.
*
* @since 8.8.0
*
* @param bool $skip_auto_install False, defaulting to "don't skip the plugin automatic installation".
* @returns bool True to skip the plugin automatic installation, false to install the plugin if necessary.
*/
if ( apply_filters( 'woocommerce_skip_legacy_rest_api_plugin_auto_install', false ) ) {
if ( apply_filters( 'woocommerce_skip_legacy_rest_api_plugin_auto_install', $previously_installed_by_us ) ) {
return;
}
@@ -1201,19 +1222,17 @@ class WC_Install {
return;
}
$plugin_name = 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php';
wp_clean_plugins_cache();
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
if ( isset( get_plugins()[ $plugin_name ] ) ) {
if ( ! ( get_site_option( 'woocommerce_autoinstalled_plugins', array() )[ $plugin_name ] ?? null ) ) {
if ( isset( get_plugins()[ $legacy_api_plugin ] ) ) {
if ( ! $previously_installed_by_us ) {
// The plugin was installed manually so let's not interfere.
return;
}
if ( in_array( $plugin_name, wp_get_active_and_valid_plugins(), true ) ) {
if ( in_array( $legacy_api_plugin, wp_get_active_and_valid_plugins(), true ) ) {
return;
}
@@ -1244,7 +1263,7 @@ class WC_Install {
$site_logs_url = get_admin_url( null, '/admin.php?page=wc-status&tab=logs' );
if ( $install_ok ) {
$activation_result = activate_plugin( $plugin_name );
$activation_result = activate_plugin( $legacy_api_plugin );
if ( $activation_result instanceof \WP_Error ) {
$message = sprintf(
/* translators: 1 = URL of Legacy REST API plugin page, 2 = URL of Legacy API settings in current site, 3 = URL of webhooks settings in current site, 4 = URL of logs page in current site, 5 = URL of plugins page in current site, 6 = URL of blog post about the Legacy REST API removal */

View File

@@ -36,7 +36,7 @@ class WC_Order_Factory {
$order_cache = wc_get_container()->get( OrderCache::class );
$order = $order_cache->get( $order_id );
if ( ! is_null( $order ) ) {
return $order;
return 0 === $order->get_id() ? false : $order;
}
}

View File

@@ -2302,9 +2302,15 @@ class WC_Order extends WC_Abstract_Order {
$refunds = $this->get_refunds();
if ( $refunds ) {
foreach ( $refunds as $id => $refund ) {
$reason = trim( $refund->get_reason() );
if ( strlen( $reason ) > 0 ) {
$reason = "<br><small>$reason</small>";
}
$total_rows[ 'refund_' . $id ] = array(
'label' => $refund->get_reason() ? $refund->get_reason() : __( 'Refund', 'woocommerce' ) . ':',
'value' => wc_price( '-' . $refund->get_amount(), array( 'currency' => $this->get_currency() ) ),
'label' => __( 'Refund', 'woocommerce' ) . ':',
'value' => wc_price( '-' . $refund->get_amount(), array( 'currency' => $this->get_currency() ) ) . $reason,
);
}
}

View File

@@ -222,7 +222,7 @@ class WC_Payment_Gateways {
The payment gateway "%2$s" was just enabled on this site:
%3$s
If this was intentional you can safely ignore and delete this email.
If this was intentional you can safely ignore and delete this email.
If you did not enable this payment gateway, please log in to your site and consider disabling it here:
%4$s
@@ -400,6 +400,8 @@ All at %6$s
* @return bool Whether PayPal Standard should be loaded or not.
*/
protected function should_load_paypal_standard() {
// Tech debt: This class needs to be initialized to make sure any existing subscriptions gets processed as expected, even if the gateway is not enabled for new orders.
// Eventually, we want to load this via a singleton pattern to avoid unnecessary instantiation.
$paypal = new WC_Gateway_Paypal();
return $paypal->should_load();
}

View File

@@ -960,7 +960,6 @@ class WC_Tracker {
'enable_myaccount_registration' => get_option( 'woocommerce_enable_myaccount_registration' ),
'registration_generate_username' => get_option( 'woocommerce_registration_generate_username' ),
'registration_generate_password' => get_option( 'woocommerce_registration_generate_password' ),
'hpos_enabled' => get_option( 'woocommerce_feature_custom_order_tables_enabled' ),
'hpos_sync_enabled' => get_option( 'woocommerce_custom_orders_table_data_sync_enabled' ),
'hpos_cot_authoritative' => get_option( 'woocommerce_custom_orders_table_enabled' ),
'hpos_transactions_enabled' => get_option( 'woocommerce_use_db_transactions_for_custom_orders_table_data_sync' ),

View File

@@ -102,7 +102,7 @@ class WC_Validation {
break;
case 'CZ':
case 'SK':
$valid = (bool) preg_match( '/^([0-9]{3})(\s?)([0-9]{2})$/', $postcode );
$valid = (bool) preg_match( "/^($country-)?([0-9]{3})(\s?)([0-9]{2})$/", $postcode );
break;
case 'NL':
$valid = (bool) preg_match( '/^([1-9][0-9]{3})(\s?)(?!SA|SD|SS)[A-Z]{2}$/i', $postcode );

View File

@@ -10,6 +10,8 @@ defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Internal\AssignDefaultCategory;
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController;
use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonCacheInvalidator;
use Automattic\WooCommerce\Internal\ComingSoon\ComingSoonRequestHandler;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
use Automattic\WooCommerce\Internal\DownloadPermissionsAdjuster;
use Automattic\WooCommerce\Internal\Features\FeaturesController;
@@ -39,7 +41,7 @@ final class WooCommerce {
*
* @var string
*/
public $version = '8.8.3';
public $version = '8.9.3';
/**
* WooCommerce Schema version.
@@ -246,7 +248,9 @@ final class WooCommerce {
add_action( 'init', array( 'WC_Emails', 'init_transactional_emails' ) );
add_action( 'init', array( $this, 'add_image_sizes' ) );
add_action( 'init', array( $this, 'load_rest_api' ) );
add_action( 'init', array( 'WC_Site_Tracking', 'init' ) );
if ( $this->is_request( 'admin' ) || ( $this->is_rest_api_request() && ! $this->is_store_api_request() ) || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
add_action( 'init', array( 'WC_Site_Tracking', 'init' ) );
}
add_action( 'switch_blog', array( $this, 'wpdb_table_fix' ), 0 );
add_action( 'activated_plugin', array( $this, 'activated_plugin' ) );
add_action( 'deactivated_plugin', array( $this, 'deactivated_plugin' ) );
@@ -255,11 +259,6 @@ final class WooCommerce {
add_action( 'woocommerce_installed', array( $this, 'add_woocommerce_remote_variant' ) );
add_action( 'woocommerce_updated', array( $this, 'add_woocommerce_remote_variant' ) );
if ( Features::is_enabled( 'launch-your-store' ) ) {
add_action( 'woocommerce_newly_installed', array( $this, 'add_lys_default_values' ) );
add_action( 'woocommerce_updated', array( $this, 'add_lys_default_values' ) );
}
// These classes set up hooks on instantiation.
$container = wc_get_container();
$container->get( ProductDownloadDirectories::class );
@@ -276,6 +275,8 @@ final class WooCommerce {
$container->get( WebhookUtil::class );
$container->get( Marketplace::class );
$container->get( TimeUtil::class );
$container->get( ComingSoonCacheInvalidator::class );
$container->get( ComingSoonRequestHandler::class );
/**
* These classes have a register method for attaching hooks.
@@ -314,35 +315,6 @@ final class WooCommerce {
}
}
/**
* Set default option values for launch your store task.
*/
public function add_lys_default_values() {
$is_new_install = current_action() === 'woocommerce_newly_installed';
$coming_soon = $is_new_install ? 'yes' : 'no';
$launch_status = $is_new_install ? 'unlaunched' : 'launched';
$store_pages_only = WCAdminHelper::is_site_fresh() ? 'no' : 'yes';
$private_link = 'yes';
$share_key = wp_generate_password( 32, false );
if ( false === get_option( 'woocommerce_coming_soon', false ) ) {
update_option( 'woocommerce_coming_soon', $coming_soon );
}
if ( false === get_option( 'woocommerce_store_pages_only', false ) ) {
update_option( 'woocommerce_store_pages_only', $store_pages_only );
}
if ( false === get_option( 'woocommerce_private_link', false ) ) {
update_option( 'woocommerce_private_link', $private_link );
}
if ( false === get_option( 'woocommerce_share_key', false ) ) {
update_option( 'woocommerce_share_key', $share_key );
}
if ( false === get_option( 'launch-status', false ) ) {
update_option( 'launch-status', $launch_status );
}
}
/**
* Ensures fatal errors are logged so they can be picked up in the status report.
*
@@ -431,7 +403,6 @@ final class WooCommerce {
* The SSR in the name is preserved for bw compatibility, as this was initially used in System Status Report.
*/
$this->define( 'WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE', 'none' );
}
/**
@@ -492,6 +463,19 @@ final class WooCommerce {
return apply_filters( 'woocommerce_is_rest_api_request', $is_rest_api_request );
}
/**
* Returns true if the request is a store REST API request.
*
* @return bool
*/
public function is_store_api_request() {
if ( empty( $_SERVER['REQUEST_URI'] ) ) {
return false;
}
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
return false !== strpos( $_SERVER['REQUEST_URI'], trailingslashit( rest_get_url_prefix() ) . 'wc/store/' );
}
/**
* Load REST API.
*/
@@ -1023,7 +1007,7 @@ final class WooCommerce {
* @param string $filename The filename of the activated plugin.
*/
public function activated_plugin( $filename ) {
include_once dirname( __FILE__ ) . '/admin/helper/class-wc-helper.php';
include_once __DIR__ . '/admin/helper/class-wc-helper.php';
if ( '/woocommerce.php' === substr( $filename, -16 ) ) {
set_transient( 'woocommerce_activated_plugin', $filename );
@@ -1039,7 +1023,7 @@ final class WooCommerce {
* @param string $filename The filename of the deactivated plugin.
*/
public function deactivated_plugin( $filename ) {
include_once dirname( __FILE__ ) . '/admin/helper/class-wc-helper.php';
include_once __DIR__ . '/admin/helper/class-wc-helper.php';
WC_Helper::deactivated_plugin( $filename );
}

View File

@@ -12,6 +12,7 @@ if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// phpcs:disable Squiz.Classes.ClassFileName.NoMatch, Squiz.Classes.ValidClassName.NotCamelCaps -- Backward compatibility.
/**
* Abstract Order Data Store: Stored in CPT.
*
@@ -80,6 +81,13 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
}
$id = wp_insert_post(
/**
* Filters the data for a new order before it is inserted into the database.
*
* @param array $data Array of data for the new order.
*
* @since 3.3.0
*/
apply_filters(
'woocommerce_new_order_data',
array(
@@ -115,7 +123,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
* @param int $order_id The order id to check.
* @return bool True if an order exists with the given name.
*/
public function order_exists( $order_id ) : bool {
public function order_exists( $order_id ): bool {
if ( ! $order_id ) {
return false;
}
@@ -135,7 +143,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
$order->set_defaults();
$post_object = get_post( $order->get_id() );
if ( ! $order->get_id() || ! $post_object || ! in_array( $post_object->post_type, wc_get_order_types(), true ) ) {
throw new Exception( __( 'Invalid order.', 'woocommerce' ) );
throw new Exception( esc_html__( 'Invalid order.', 'woocommerce' ) );
}
$this->set_order_props(
@@ -291,7 +299,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
/**
* Fires immediately after an order is deleted.
*
* @since
* @since 2.7.0
*
* @param int $order_id ID of the order that has been deleted.
*/
@@ -345,6 +353,13 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
$order_status = $order->get_status( 'edit' );
if ( ! $order_status ) {
/**
* Filters the default order status to use when creating a new order.
*
* @param string $order_status Default order status.
*
* @since 3.7.0
*/
$order_status = apply_filters( 'woocommerce_default_order_status', 'pending' );
}
@@ -352,7 +367,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
$valid_statuses = get_post_stati();
// Add a wc- prefix to the status, but exclude some core statuses which should not be prefixed.
// @todo In the future this should only happen based on `wc_is_order_status`, but in order to
// In the future this should only happen based on `wc_is_order_status`, but in order to
// preserve back-compatibility this happens to all statuses except a select few. A doing_it_wrong
// Notice will be needed here, followed by future removal.
if ( ! in_array( $post_status, array( 'auto-draft', 'draft', 'trash' ), true ) && in_array( 'wc-' . $post_status, $valid_statuses, true ) ) {
@@ -380,7 +395,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
protected function get_post_title() {
// @codingStandardsIgnoreStart
/* translators: %s: Order date */
return sprintf( __( 'Order &ndash; %s', 'woocommerce' ), (new DateTime('now'))->format( _x( 'M d, Y @ h:i A', 'Order date parsed by DateTime::format', 'woocommerce' ) ) );
return sprintf( __( 'Order &ndash; %s', 'woocommerce' ), ( new DateTime( 'now' ) )->format( _x( 'M d, Y @ h:i A', 'Order date parsed by DateTime::format', 'woocommerce' ) ) );
// @codingStandardsIgnoreEnd
}
@@ -466,6 +481,14 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
}
}
/**
* Action fired after updating order properties.
*
* @param WC_Abstract_Order $order Order object.
* @param string[] $updated_props Array of updated properties.
*
* @since 2.7.0
*/
do_action( 'woocommerce_order_object_updated_props', $order, $updated_props );
}
@@ -491,6 +514,11 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
public function read_items( $order, $type ) {
global $wpdb;
// When the order is not yet saved, we cannot get the items from DB. Trying to do so will risk reading items of different orders that were saved incorrectly.
if ( 0 === $order->get_id() ) {
return array();
}
// Get from cache if available.
$items = 0 < $order->get_id() ? wp_cache_get( 'order-items-' . $order->get_id(), 'orders' ) : false;

View File

@@ -94,19 +94,22 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust
* @param array $allowed_keys The allowed meta data keys.
* @param WC_Customer $customer The customer object.
*/
$allowed_keys = apply_filters( 'woocommerce_customer_allowed_session_meta_keys', array(), $customer );
$session_value = wp_json_encode(
array_filter(
$customer->get_meta_data(),
function( $meta_data ) use ( $allowed_keys ) {
return in_array( $meta_data->key, $allowed_keys, true );
}
)
);
$allowed_keys = apply_filters( 'woocommerce_customer_allowed_session_meta_keys', array(), $customer );
$session_value = array();
foreach ( $customer->get_meta_data() as $meta_data ) {
if ( in_array( $meta_data->key, $allowed_keys, true ) ) {
$session_value[] = array(
'key' => $meta_data->key,
'value' => $meta_data->value,
);
}
}
$data['meta_data'] = $session_value;
} else {
$session_value = $customer->{"get_$function_key"}( 'edit' );
$session_value = $customer->{"get_$function_key"}( 'edit' );
$data[ $session_key ] = (string) $session_value;
}
$data[ $session_key ] = (string) $session_value;
}
WC()->session->set( 'customer', $data );
}
@@ -137,9 +140,8 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust
}
if ( ! empty( $data[ $session_key ] ) && is_callable( array( $customer, "set_{$function_key}" ) ) ) {
if ( 'meta_data' === $session_key ) {
$meta_data_values = json_decode( wp_unslash( $data[ $session_key ] ), true );
if ( $meta_data_values ) {
foreach ( $meta_data_values as $meta_data_value ) {
if ( is_array( $data[ $session_key ] ) ) {
foreach ( $data[ $session_key ] as $meta_data_value ) {
if ( ! isset( $meta_data_value['key'], $meta_data_value['value'] ) ) {
continue;
}

View File

@@ -1193,10 +1193,11 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
* @todo Add to interface in 4.0.
* @param WC_Product $product Variable product.
* @param int $limit Limit the number of created variations.
* @param array $default_values Key value pairs to set on created variations.
* @param array $default_values Key value pairs to set on created variations.
* @param array $metadata Key value pairs to set as meta data on created variations.
* @return int Number of created variations.
*/
public function create_all_product_variations( $product, $limit = -1, $default_values = array() ) {
public function create_all_product_variations( $product, $limit = -1, $default_values = array(), $metadata = array() ) {
$count = 0;
if ( ! $product ) {
@@ -1226,6 +1227,9 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
}
$variation = wc_get_product_object( 'variation' );
$variation->set_props( $default_values );
foreach ( $metadata as $meta ) {
$variation->add_meta_data( $meta['key'], $meta['value'] );
}
$variation->set_parent_id( $product->get_id() );
$variation->set_attributes( $possible_attribute );
$variation_id = $variation->save();

View File

@@ -527,27 +527,32 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
$should_load = $this->get_option( $option_key );
if ( '' === $should_load ) {
// New installs without PayPal Standard enabled don't load it.
if ( 'no' === $this->enabled && WC_Install::is_new_install() ) {
$should_load = false;
} else {
$should_load = true;
}
// Set default `_should_load` to 'yes' on existing stores with PayPal Standard enabled or with existing PayPal Standard orders.
$should_load = 'yes' === $this->enabled || $this->has_paypal_orders();
$this->update_option( $option_key, wc_bool_to_string( $should_load ) );
} else {
$should_load = wc_string_to_bool( $should_load );
// Enabled always takes precedence over the option.
$should_load = wc_string_to_bool( $this->enabled ) || wc_string_to_bool( $should_load );
}
/**
* Allow third-parties to filter whether PayPal Standard should be loaded or not.
*
* @since 5.5.0
*
* @param bool $should_load Whether PayPal Standard should be loaded.
* @param WC_Gateway_Paypal $this The WC_Gateway_Paypal instance.
*/
return apply_filters( 'woocommerce_should_load_paypal_standard', $should_load, $this );
return $should_load;
}
/**
* Checks if the store has at least one PayPal Standand order.
*
* @return bool
*/
public function has_paypal_orders() {
$paypal_orders = wc_get_orders(
array(
'limit' => 1,
'return' => 'ids',
'payment_method' => 'paypal',
)
);
return is_countable( $paypal_orders ) ? 1 === count( $paypal_orders ) : false;
}
}

View File

@@ -747,7 +747,7 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
// Unlimited, set to 32GB.
$memory_limit = '32000M';
}
return intval( $memory_limit ) * 1024 * 1024;
return wp_convert_hr_to_bytes( $memory_limit );
}
/**

View File

@@ -29,7 +29,7 @@ if ( ! function_exists( 'wc_admin_get_feature_config' ) ) {
'product-grouped' => true,
'product-linked' => true,
'product-pre-publish-modal' => true,
'product-custom-fields' => false,
'product-custom-fields' => true,
'remote-inbox-notifications' => true,
'remote-free-extensions' => true,
'payment-gateway-suggestions' => true,

View File

@@ -569,12 +569,6 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
'context' => array( 'view' ),
'readonly' => true,
),
'HPOS_feature_screen_enabled' => array(
'description' => __( 'Is HPOS feature screen enabled?', 'woocommerce' ),
'type' => 'boolean',
'context' => array( 'view' ),
'readonly' => true,
),
'HPOS_enabled' => array(
'description' => __( 'Is HPOS enabled?', 'woocommerce' ),
'type' => 'boolean',
@@ -1349,7 +1343,6 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
'woocommerce_com_connected' => ConnectionHelper::is_connected() ? 'yes' : 'no',
'enforce_approved_download_dirs' => wc_get_container()->get( Download_Directories::class )->get_mode() === Download_Directories::MODE_ENABLED,
'order_datastore' => WC_Data_Store::load( 'order' )->get_current_class_name(),
'HPOS_feature_screen_enabled' => wc_get_container()->get( Automattic\WooCommerce\Internal\Features\FeaturesController::class )->feature_is_enabled( 'custom_order_tables' ),
'HPOS_enabled' => OrderUtil::custom_orders_table_usage_is_enabled(),
'HPOS_sync_enabled' => wc_get_container()->get( Order_DataSynchronizer::class )->data_sync_is_enabled(),
);

View File

@@ -1121,8 +1121,9 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
$response = array();
$product = wc_get_product( $product_id );
$default_values = isset( $request['default_values'] ) ? $request['default_values'] : array();
$meta_data = isset( $request['meta_data'] ) ? $request['meta_data'] : array();
$data_store = $product->get_data_store();
$response['count'] = $data_store->create_all_product_variations( $product, Constants::get_constant( 'WC_MAX_LINKED_VARIATIONS' ), $default_values );
$response['count'] = $data_store->create_all_product_variations( $product, Constants::get_constant( 'WC_MAX_LINKED_VARIATIONS' ), $default_values, $meta_data );
if ( isset( $request['delete'] ) && $request['delete'] ) {
$deleted_count = $this->delete_unmatched_product_variations( $product );

View File

@@ -62,6 +62,54 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
'schema' => array( $this, 'get_public_item_schema' ),
)
);
register_rest_route(
$this->namespace,
'/' . $this->rest_base . '/(?P<id>[\d]+)/duplicate',
array(
'args' => array(
'id' => array(
'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
'type' => 'integer',
),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'duplicate_product' ),
'permission_callback' => array( $this, 'create_item_permissions_check' ),
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}
/**
* Duplicate a product and returns the duplicated product.
* The product status is set to "draft" and the name includes a "(copy)" at the end by default.
*
* @param WP_REST_Request $request Request data.
* @return WP_REST_Response|WP_Error
*/
public function duplicate_product( $request ) {
$product_id = $request->get_param( 'id' );
$product = wc_get_product( $product_id );
if ( ! $product ) {
return new WP_Error( 'woocommerce_rest_product_invalid_id', __( 'Invalid product ID.', 'woocommerce' ), array( 'status' => 404 ) );
}
// Creating product object from request data in preparation for copying.
$updated_product = $this->prepare_object_for_database( $request );
$duplicated_product = ( new WC_Admin_Duplicate_Product() )->product_duplicate( $updated_product );
if ( is_wp_error( $duplicated_product ) ) {
return new WP_Error( 'woocommerce_rest_product_duplicate_error', $duplicated_product->get_error_message(), array( 'status' => 400 ) );
}
$response_data = $duplicated_product->get_data();
return new WP_REST_Response( $response_data, 200 );
}
/**
@@ -325,7 +373,7 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
global $wpdb;
if ( ! empty( $this->search_sku_in_product_lookup_table ) ) {
$like_search = '%' . $wpdb->esc_like( $this->search_sku_in_product_lookup_table ) . '%';
$where .= ' AND ' . $wpdb->prepare( '(wc_product_meta_lookup.sku LIKE %s)', $like_search );
$where .= ' AND ' . $wpdb->prepare( '(wc_product_meta_lookup.sku LIKE %s)', $like_search );
}
return $where;
}
@@ -1112,7 +1160,7 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'low_stock_amount' => array(
'low_stock_amount' => array(
'description' => __( 'Low Stock amount for the product.', 'woocommerce' ),
'type' => array( 'integer', 'null' ),
'context' => array( 'view', 'edit' ),
@@ -1344,7 +1392,7 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
),
),
),
'has_options' => array(
'has_options' => array(
'description' => __( 'Shows if the product needs to be configured before it can be bought.', 'woocommerce' ),
'type' => 'boolean',
'context' => array( 'view', 'edit' ),

View File

@@ -8,6 +8,8 @@
* @since 3.5.0
*/
use Automattic\WooCommerce\Utilities\OrderUtil;
defined( 'ABSPATH' ) || exit;
/**
@@ -39,18 +41,18 @@ class WC_REST_Report_Orders_Totals_Controller extends WC_REST_Reports_Controller
* @return array
*/
protected function get_reports() {
$totals = wp_count_posts( 'shop_order' );
$totals = OrderUtil::get_count_for_type( 'shop_order' );
$data = array();
foreach ( wc_get_order_statuses() as $slug => $name ) {
if ( ! isset( $totals->$slug ) ) {
if ( ! array_key_exists( $slug, $totals ) ) {
continue;
}
$data[] = array(
'slug' => str_replace( 'wc-', '', $slug ),
'name' => $name,
'total' => (int) $totals->$slug,
'total' => (int) $totals[ $slug ],
);
}

View File

@@ -393,7 +393,7 @@ function wc_get_account_saved_payment_methods_list_item_cc( $item, $payment_toke
$card_type = $payment_token->get_card_type();
$item['method']['last4'] = $payment_token->get_last4();
$item['method']['brand'] = ( ! empty( $card_type ) ? ucfirst( $card_type ) : esc_html__( 'Credit card', 'woocommerce' ) );
$item['method']['brand'] = ( ! empty( $card_type ) ? ucwords( str_replace( '_', ' ', $card_type ) ) : esc_html__( 'Credit card', 'woocommerce' ) );
$item['expires'] = $payment_token->get_expiry_month() . '/' . substr( $payment_token->get_expiry_year(), -2 );
return $item;

View File

@@ -283,8 +283,6 @@ function wc_cart_totals_coupon_html( $coupon ) {
$coupon = new WC_Coupon( $coupon );
}
$discount_amount_html = '';
$amount = WC()->cart->get_coupon_discount_amount( $coupon->get_code(), WC()->cart->display_cart_ex_tax );
$discount_amount_html = '-' . wc_price( $amount );

View File

@@ -1539,12 +1539,18 @@ function wc_get_credit_card_type_label( $type ) {
'visa' => _x( 'Visa', 'Name of credit card', 'woocommerce' ),
'discover' => _x( 'Discover', 'Name of credit card', 'woocommerce' ),
'american express' => _x( 'American Express', 'Name of credit card', 'woocommerce' ),
'cartes bancaires' => _x( 'Cartes Bancaires', 'Name of credit card', 'woocommerce' ),
'diners' => _x( 'Diners', 'Name of credit card', 'woocommerce' ),
'jcb' => _x( 'JCB', 'Name of credit card', 'woocommerce' ),
)
);
return apply_filters( 'woocommerce_get_credit_card_type_label', ( array_key_exists( $type, $labels ) ? $labels[ $type ] : ucfirst( $type ) ) );
/**
* Fallback to title case, uppercasing the first letter of each word.
*
* @since 8.9.0
*/
return apply_filters( 'woocommerce_get_credit_card_type_label', ( array_key_exists( $type, $labels ) ? $labels[ $type ] : ucwords( $type ) ) );
}
/**
@@ -1554,7 +1560,7 @@ function wc_get_credit_card_type_label( $type ) {
* @param string $url URL of the page to return to.
*/
function wc_back_link( $label, $url ) {
echo '<small class="wc-admin-breadcrumb"><a href="' . esc_url( $url ) . '" aria-label="' . esc_attr( $label ) . '">&#x2934;</a></small>';
echo '<small class="wc-admin-breadcrumb"><a href="' . esc_url( $url ) . '" aria-label="' . esc_attr( $label ) . '">&#x2934;&#xfe0e;</a></small>';
}
/**

View File

@@ -979,6 +979,9 @@ function wc_format_postcode( $postcode, $country ) {
$postcode = wc_normalize_postcode( $postcode ?? '' );
switch ( $country ) {
case 'SE':
$postcode = substr_replace( $postcode, ' ', -2, 0 );
break;
case 'CA':
case 'GB':
$postcode = substr_replace( $postcode, ' ', -3, 0 );
@@ -1004,9 +1007,12 @@ function wc_format_postcode( $postcode, $country ) {
$postcode = substr_replace( $postcode, ' ', 4, 0 );
break;
case 'LV':
if ( preg_match( '/(?:LV)?-?(\d+)/i', $postcode, $matches ) ) {
$postcode = count( $matches ) >= 2 ? "LV-$matches[1]" : $postcode;
}
$postcode = preg_replace( '/^(LV)?-?(\d+)$/', 'LV-${2}', $postcode );
break;
case 'CZ':
case 'SK':
$postcode = preg_replace( "/^({$country})-?(\d+)$/", '${1}-${2}', $postcode );
$postcode = substr_replace( $postcode, ' ', -2, 0 );
break;
case 'DK':
$postcode = preg_replace( '/^(DK)(.+)$/', '${1}-${2}', $postcode );

View File

@@ -363,3 +363,13 @@ function wc_rest_check_product_reviews_permissions( $context = 'read', $object_i
return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, 'product_review' );
}
/**
* Returns true if the current REST request is from the product editor.
*
* @since 8.9.0
* @return bool
*/
function wc_rest_is_from_product_editor() {
return isset( $_SERVER['HTTP_X_WC_FROM_PRODUCT_EDITOR'] ) && '1' === $_SERVER['HTTP_X_WC_FROM_PRODUCT_EDITOR'];
}

View File

@@ -9,6 +9,7 @@
*/
use Automattic\Jetpack\Constants;
use Automattic\WooCommerce\Internal\Utilities\HtmlSanitizer;
defined( 'ABSPATH' ) || exit;
@@ -896,10 +897,11 @@ function wc_terms_and_conditions_page_content() {
return;
}
$page = get_post( $terms_page_id );
$sanitizer = wc_get_container()->get( HtmlSanitizer::class );
$page = get_post( $terms_page_id );
if ( $page && 'publish' === $page->post_status && $page->post_content && ! has_shortcode( $page->post_content, 'woocommerce_checkout' ) ) {
echo '<div class="woocommerce-terms-and-conditions" style="display: none; max-height: 200px; overflow: auto;">' . wc_format_content( wp_kses_post( $page->post_content ) ) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="woocommerce-terms-and-conditions" style="display: none; max-height: 200px; overflow: auto;">' . wc_format_content( $sanitizer->styled_post_content( $page->post_content ) ) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}

View File

@@ -18,8 +18,11 @@
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\Notes\Note;
use Automattic\WooCommerce\Admin\Notes\Notes;
use Automattic\WooCommerce\Database\Migrations\MigrationHelper;
use Automattic\WooCommerce\Internal\Admin\Marketing\MarketingSpecs;
use Automattic\WooCommerce\Internal\Admin\Notes\WooSubscriptionsNotes;
use Automattic\WooCommerce\Internal\AssignDefaultCategory;
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
@@ -167,7 +170,7 @@ function wc_update_200_taxrates() {
)
);
$loop++;
++$loop;
}
}
}
@@ -216,7 +219,7 @@ function wc_update_200_taxrates() {
}
}
$loop++;
++$loop;
}
}
@@ -2571,7 +2574,6 @@ function wc_update_750_add_columns_to_order_stats_table() {
and postmeta.meta_key = '_date_completed'
SET order_stats.date_completed = IFNULL(FROM_UNIXTIME(postmeta.meta_value), '0000-00-00 00:00:00');"
);
}
/**
@@ -2667,3 +2669,58 @@ function wc_update_870_prevent_listing_of_transient_files_directory() {
$wp_filesystem->put_contents( $default_transient_files_dir . '/.htaccess', 'deny from all' );
$wp_filesystem->put_contents( $default_transient_files_dir . '/index.html', '' );
}
/**
* If it exists, remove and recreate the inbox note that asks users to connect to `Woo.com` so that the domain name is changed to the updated `WooCommerce.com`.
*/
function wc_update_890_update_connect_to_woocommerce_note() {
$note = Notes::get_note_by_name( WooSubscriptionsNotes::CONNECTION_NOTE_NAME );
if ( ! is_a( $note, 'Automattic\WooCommerce\Admin\Notes\Note' ) ) {
return;
}
if ( ! str_contains( $note->get_title(), 'Woo.com' ) ) {
return;
}
if ( $note->get_status() !== Note::E_WC_ADMIN_NOTE_SNOOZED && $note->get_status() !== Note::E_WC_ADMIN_NOTE_UNACTIONED ) {
return;
}
Notes::delete_notes_with_name( WooSubscriptionsNotes::CONNECTION_NOTE_NAME );
$new_note = WooSubscriptionsNotes::get_note();
$new_note->save();
}
/**
* Disables the PayPal Standard gateway for stores that aren't using it.
*
* PayPal Standard has been deprecated since WooCommerce 5.5, but there are some stores that have it showing up in their
* list of available Payment methods even if it's not setup. In WooComerce 8.9 we will disable PayPal Standard for those stores
* to reduce the amount of new connections to the legacy gateway.
*
* Shows an admin notice to inform the store owner that PayPal Standard has been disabled and suggests installing PayPal Payments.
*/
function wc_update_890_update_paypal_standard_load_eligibility() {
$paypal = class_exists( 'WC_Gateway_Paypal' ) ? new WC_Gateway_Paypal() : null;
if ( ! $paypal ) {
return;
}
// If PayPal is enabled or set to load, but the store hasn't setup PayPal Standard live API keys and doesn't have any PayPal Orders, disable it.
if ( ( 'yes' === $paypal->enabled || 'yes' === $paypal->get_option( '_should_load' ) ) && ! $paypal->get_option( 'api_username' ) && ! $paypal->has_paypal_orders() ) {
$paypal->update_option( '_should_load', wc_bool_to_string( false ) );
}
}
/**
* Create the woocommerce_history_of_autoinstalled_plugins option if it doesn't exist
* as a copy of woocommerce_autoinstalled_plugins if it exists.
*/
function wc_update_891_create_plugin_autoinstall_history_option() {
$autoinstalled_plugins_history_info = get_site_option( 'woocommerce_history_of_autoinstalled_plugins' );
if ( false === $autoinstalled_plugins_history_info ) {
$autoinstalled_plugins_info = get_site_option( 'woocommerce_autoinstalled_plugins' );
if ( false !== $autoinstalled_plugins_info ) {
update_site_option( 'woocommerce_history_of_autoinstalled_plugins', $autoinstalled_plugins_info );
}
}
}

View File

@@ -159,6 +159,11 @@ function wc_get_webhook_statuses() {
* @return bool
*/
function wc_load_webhooks( $status = '', $limit = null ) {
// short-circuit if webhooks should not be loaded at all.
if ( ! is_null( $limit ) && $limit <= 0 ) {
return false;
}
$data_store = WC_Data_Store::load( 'webhook' );
$webhooks = $data_store->get_webhooks_ids( $status );
$loaded = 0;