Plugin Updates
This commit is contained in:
@@ -1365,8 +1365,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
} else {
|
||||
|
||||
// If we do not have a coupon ID (was it virtual? has it been deleted?) we must create a temporary coupon using what data we have stored during checkout.
|
||||
$coupon_object = new WC_Coupon();
|
||||
$coupon_object->set_props( (array) $coupon_item->get_meta( 'coupon_data', true ) );
|
||||
$coupon_object = $this->get_temporary_coupon( $coupon_item );
|
||||
$coupon_object->set_code( $coupon_code );
|
||||
$coupon_object->set_virtual( true );
|
||||
|
||||
@@ -1402,6 +1401,35 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
$this->calculate_totals( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a coupon object populated from order line item metadata, to be used when reapplying coupons
|
||||
* if the original coupon no longer exists.
|
||||
*
|
||||
* @since 8.7.0
|
||||
*
|
||||
* @param WC_Order_Item_Coupon $coupon_item The order item corresponding to the coupon to reapply.
|
||||
* @returns WC_Coupon Coupon object populated from order line item metadata, or empty if no such metadata exists (should never happen).
|
||||
*/
|
||||
private function get_temporary_coupon( WC_Order_Item_Coupon $coupon_item ): WC_Coupon {
|
||||
$coupon_object = new WC_Coupon();
|
||||
|
||||
// Since WooCommerce 8.7 a succint 'coupon_info' line item meta entry is created
|
||||
// whenever a coupon is applied to an order. Previously a more verbose 'coupon_data' was created.
|
||||
|
||||
$coupon_info = $coupon_item->get_meta( 'coupon_info', true );
|
||||
if ( $coupon_info ) {
|
||||
$coupon_object->set_short_info( $coupon_info );
|
||||
return $coupon_object;
|
||||
}
|
||||
|
||||
$coupon_data = $coupon_item->get_meta( 'coupon_data', true );
|
||||
if ( $coupon_data ) {
|
||||
$coupon_object->set_props( (array) $coupon_data );
|
||||
}
|
||||
|
||||
return $coupon_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* After applying coupons via the WC_Discounts class, update line items.
|
||||
*
|
||||
@@ -1461,11 +1489,8 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
$coupon_id = wc_get_coupon_id_by_code( $coupon_code );
|
||||
$coupon = new WC_Coupon( $coupon_id );
|
||||
|
||||
// Avoid storing used_by - it's not needed and can get large.
|
||||
$coupon_data = $coupon->get_data();
|
||||
unset( $coupon_data['used_by'] );
|
||||
|
||||
$coupon_item->add_meta_data( 'coupon_data', $coupon_data );
|
||||
$coupon_info = $coupon->get_short_info();
|
||||
$coupon_item->add_meta_data( 'coupon_info', $coupon_info );
|
||||
} else {
|
||||
$coupon_item = $this->get_item( $item_id, false );
|
||||
}
|
||||
|
||||
@@ -79,31 +79,19 @@ class WC_Admin_Addons {
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public static function fetch_featured() {
|
||||
$transient_name = 'wc_addons_featured';
|
||||
// Important: WCCOM Extensions API v2.0 is used.
|
||||
$url = 'https://woocommerce.com/wp-json/wccom-extensions/2.0/featured';
|
||||
$locale = get_user_locale();
|
||||
$featured = self::get_locale_data_from_transient( 'wc_addons_featured', $locale );
|
||||
$featured = self::get_locale_data_from_transient( $transient_name, $locale );
|
||||
|
||||
if ( false === $featured ) {
|
||||
$headers = array();
|
||||
$auth = WC_Helper_Options::get( 'auth' );
|
||||
|
||||
if ( ! empty( $auth['access_token'] ) ) {
|
||||
$headers['Authorization'] = 'Bearer ' . $auth['access_token'];
|
||||
}
|
||||
|
||||
$parameter_string = '?' . http_build_query( array( 'locale' => get_user_locale() ) );
|
||||
$country = WC()->countries->get_base_country();
|
||||
if ( ! empty( $country ) ) {
|
||||
$parameter_string = $parameter_string . '&' . http_build_query( array( 'country' => $country ) );
|
||||
}
|
||||
|
||||
// Important: WCCOM Extensions API v2.0 is used.
|
||||
$raw_featured = wp_safe_remote_get(
|
||||
'https://woocommerce.com/wp-json/wccom-extensions/2.0/featured' . $parameter_string,
|
||||
array(
|
||||
'headers' => $headers,
|
||||
'user-agent' => 'WooCommerce/' . WC()->version . '; ' . get_bloginfo( 'url' ),
|
||||
)
|
||||
$fetch_options = array(
|
||||
'auth' => true,
|
||||
'locale' => true,
|
||||
'country' => true,
|
||||
);
|
||||
$raw_featured = self::fetch( $url, $fetch_options );
|
||||
|
||||
if ( is_wp_error( $raw_featured ) ) {
|
||||
do_action( 'woocommerce_page_wc-addons_connection_error', $raw_featured->get_error_message() );
|
||||
@@ -143,7 +131,7 @@ class WC_Admin_Addons {
|
||||
}
|
||||
|
||||
if ( $featured ) {
|
||||
self::set_locale_data_in_transient( 'wc_addons_featured', $featured, $locale, DAY_IN_SECONDS );
|
||||
self::set_locale_data_in_transient( $transient_name, $featured, $locale, DAY_IN_SECONDS );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1556,4 +1544,49 @@ class WC_Admin_Addons {
|
||||
$transient_value[ $locale ] = $value;
|
||||
return set_transient( $transient, $transient_value, $expiration );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make wp_safe_remote_get request to Woo.com endpoint.
|
||||
* Optionally pass user auth token, locale or country.
|
||||
*
|
||||
* @param string $url URL to request.
|
||||
* @param ?array $options Options for the request. For example, to pass auth token, locale and country,
|
||||
* pass array( 'auth' => true, 'locale' => true, 'country' => true, ).
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public static function fetch( $url, $options = array() ) {
|
||||
$headers = array();
|
||||
|
||||
if ( isset( $options['auth'] ) && $options['auth'] ) {
|
||||
$auth = WC_Helper_Options::get( 'auth' );
|
||||
|
||||
if ( isset( $auth['access_token'] ) && ! empty( $auth['access_token'] ) ) {
|
||||
$headers['Authorization'] = 'Bearer ' . $auth['access_token'];
|
||||
}
|
||||
}
|
||||
|
||||
$parameters = array();
|
||||
|
||||
if ( isset( $options['locale'] ) && $options['locale'] ) {
|
||||
$parameters['locale'] = get_user_locale();
|
||||
}
|
||||
|
||||
if ( isset( $options['country'] ) && $options['country'] ) {
|
||||
$country = WC()->countries->get_base_country();
|
||||
if ( ! empty( $country ) ) {
|
||||
$parameters['country'] = $country;
|
||||
}
|
||||
}
|
||||
|
||||
$query_string = ! empty( $parameters ) ? '?' . http_build_query( $parameters ) : '';
|
||||
|
||||
return wp_safe_remote_get(
|
||||
$url . $query_string,
|
||||
array(
|
||||
'headers' => $headers,
|
||||
'user-agent' => 'WooCommerce/' . WC()->version . '; ' . get_bloginfo( 'url' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,6 +554,22 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
);
|
||||
wp_enqueue_script( 'marketplace-suggestions' );
|
||||
}
|
||||
|
||||
// Marketplace promotions.
|
||||
if ( in_array( $screen_id, array( 'woocommerce_page_wc-admin' ), true ) ) {
|
||||
|
||||
$promotions = get_transient( WC_Admin_Marketplace_Promotions::TRANSIENT_NAME );
|
||||
|
||||
if ( false === $promotions ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_add_inline_script(
|
||||
'wc-admin-app',
|
||||
'window.wcMarketplace = ' . wp_json_encode( array( 'promotions' => $promotions ) ),
|
||||
'before'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -293,11 +293,11 @@ class WC_Admin_Importers {
|
||||
'position' => 'done',
|
||||
'percentage' => 100,
|
||||
'url' => add_query_arg( array( '_wpnonce' => wp_create_nonce( 'woocommerce-csv-importer' ) ), admin_url( 'edit.php?post_type=product&page=product_importer&step=done' ) ),
|
||||
'imported' => count( $results['imported'] ),
|
||||
'imported_variations' => count( $results['imported_variations'] ),
|
||||
'failed' => count( $results['failed'] ),
|
||||
'updated' => count( $results['updated'] ),
|
||||
'skipped' => count( $results['skipped'] ),
|
||||
'imported' => is_countable( $results['imported'] ) ? count( $results['imported'] ) : 0,
|
||||
'imported_variations' => is_countable( $results['imported_variations'] ) ? count( $results['imported_variations'] ) : 0,
|
||||
'failed' => is_countable( $results['failed'] ) ? count( $results['failed'] ) : 0,
|
||||
'updated' => is_countable( $results['updated'] ) ? count( $results['updated'] ) : 0,
|
||||
'skipped' => is_countable( $results['skipped'] ) ? count( $results['skipped'] ) : 0,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
@@ -305,11 +305,11 @@ class WC_Admin_Importers {
|
||||
array(
|
||||
'position' => $importer->get_file_position(),
|
||||
'percentage' => $percent_complete,
|
||||
'imported' => count( $results['imported'] ),
|
||||
'imported_variations' => count( $results['imported_variations'] ),
|
||||
'failed' => count( $results['failed'] ),
|
||||
'updated' => count( $results['updated'] ),
|
||||
'skipped' => count( $results['skipped'] ),
|
||||
'imported' => is_countable( $results['imported'] ) ? count( $results['imported'] ) : 0,
|
||||
'imported_variations' => is_countable( $results['imported_variations'] ) ? count( $results['imported_variations'] ) : 0,
|
||||
'failed' => is_countable( $results['failed'] ) ? count( $results['failed'] ) : 0,
|
||||
'updated' => is_countable( $results['updated'] ) ? count( $results['updated'] ) : 0,
|
||||
'skipped' => is_countable( $results['skipped'] ) ? count( $results['skipped'] ) : 0,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
/**
|
||||
* Addons Page
|
||||
*
|
||||
* @package WooCommerce\Admin
|
||||
* @version 2.5.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Admin_Marketplace_Promotions class.
|
||||
*/
|
||||
class WC_Admin_Marketplace_Promotions {
|
||||
|
||||
const TRANSIENT_NAME = 'woocommerce_marketplace_promotions';
|
||||
const SCHEDULED_ACTION_HOOK = 'woocommerce_marketplace_fetch_promotions';
|
||||
/**
|
||||
* The user's locale, for example en_US.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public static string $locale;
|
||||
|
||||
/**
|
||||
* On all admin pages, schedule an action to fetch promotions data.
|
||||
* Add menu badge to WooCommerce Extensions item if the promotions
|
||||
* API requests one.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function init_marketplace_promotions() {
|
||||
// Add the callback for our scheduled action.
|
||||
if ( ! has_action( self::SCHEDULED_ACTION_HOOK, array( __CLASS__, 'fetch_marketplace_promotions' ) ) ) {
|
||||
add_action( self::SCHEDULED_ACTION_HOOK, array( __CLASS__, 'fetch_marketplace_promotions' ) );
|
||||
}
|
||||
|
||||
if ( self::is_admin_page() ) {
|
||||
// Schedule the action twice a day, starting now.
|
||||
if ( false === wp_next_scheduled( self::SCHEDULED_ACTION_HOOK ) ) {
|
||||
wp_schedule_event( time(), 'twicedaily', self::SCHEDULED_ACTION_HOOK );
|
||||
}
|
||||
|
||||
self::$locale = ( self::$locale ?? get_user_locale() ) ?? 'en_US';
|
||||
self::maybe_show_bubble_promotions();
|
||||
}
|
||||
|
||||
register_deactivation_hook( WC_PLUGIN_FILE, array( __CLASS__, 'clear_scheduled_event' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the request is for an admin page, and not ajax.
|
||||
* We may want to add a menu bubble to WooCommerce Extensions
|
||||
* on any admin page, as the user may view the WooCommerce flyout
|
||||
* menu.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_admin_page(): bool {
|
||||
if (
|
||||
( defined( 'DOING_AJAX' ) && DOING_AJAX )
|
||||
|| ! is_admin()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get promotions to show in the Woo in-app marketplace.
|
||||
* Only run on selected pages in the main WooCommerce menu in wp-admin.
|
||||
* Loads promotions in transient with one day life.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function fetch_marketplace_promotions() {
|
||||
$url = 'https://woo.com/wp-json/wccom-extensions/3.0/promotions';
|
||||
$promotions = get_transient( self::TRANSIENT_NAME );
|
||||
|
||||
if ( false !== $promotions ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fetch_options = array(
|
||||
'auth' => true,
|
||||
'country' => true,
|
||||
);
|
||||
$raw_promotions = WC_Admin_Addons::fetch( $url, $fetch_options );
|
||||
|
||||
// phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
if ( is_wp_error( $raw_promotions ) ) {
|
||||
/**
|
||||
* Allows connection error to be handled.
|
||||
*
|
||||
* @since 8.7
|
||||
*/
|
||||
do_action( 'woocommerce_page_wc-addons_connection_error', $raw_promotions->get_error_message() );
|
||||
}
|
||||
|
||||
$response_code = (int) wp_remote_retrieve_response_code( $raw_promotions );
|
||||
if ( 200 !== $response_code ) {
|
||||
/**
|
||||
* Allows connection error to be handled.
|
||||
*
|
||||
* @since 8.7
|
||||
*/
|
||||
do_action( 'woocommerce_page_wc-addons_connection_error', $response_code );
|
||||
}
|
||||
|
||||
$promotions = json_decode( wp_remote_retrieve_body( $raw_promotions ), true );
|
||||
if ( empty( $promotions ) || ! is_array( $promotions ) ) {
|
||||
/**
|
||||
* Allows connection error to be handled.
|
||||
*
|
||||
* @since 8.7
|
||||
*/
|
||||
do_action( 'woocommerce_page_wc-addons_connection_error', 'Empty or malformed response' );
|
||||
}
|
||||
// phpcs:enable WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
|
||||
if ( $promotions ) {
|
||||
// Filter out any expired promotions.
|
||||
$promotions = self::get_active_promotions( $promotions );
|
||||
set_transient( self::TRANSIENT_NAME, $promotions, DAY_IN_SECONDS );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there's an active promotion of the format `menu_bubble`,
|
||||
* add a filter to show a bubble on the Extensions item in the
|
||||
* WooCommerce menu.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception If we are unable to create a DateTime from the date_to_gmt.
|
||||
*/
|
||||
private static function maybe_show_bubble_promotions() {
|
||||
$promotions = get_transient( self::TRANSIENT_NAME );
|
||||
if ( ! $promotions ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$bubble_promotions = self::get_promotions_of_format( $promotions, 'menu_bubble' );
|
||||
if ( empty( $bubble_promotions ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now_date_time = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
|
||||
|
||||
// Let's make absolutely sure the promotion is still active.
|
||||
foreach ( $bubble_promotions as $promotion ) {
|
||||
if ( ! isset( $promotion['date_to_gmt'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$date_to_gmt = new DateTime( $promotion['date_to_gmt'], new DateTimeZone( 'UTC' ) );
|
||||
} catch ( \Exception $ex ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $now_date_time < $date_to_gmt ) {
|
||||
add_filter(
|
||||
'woocommerce_marketplace_menu_items',
|
||||
function ( $marketplace_pages ) use ( $promotion ) {
|
||||
return self::filter_marketplace_menu_items( $marketplace_pages, $promotion );
|
||||
}
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From the array of promotions, select those of a given format.
|
||||
*
|
||||
* @param ? array $promotions Array of data about promotions of all formats.
|
||||
* @param ? string $format Format we want to filter for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_promotions_of_format( $promotions = array(), $format = '' ): array {
|
||||
if ( empty( $promotions ) || empty( $format ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_filter(
|
||||
$promotions,
|
||||
function( $promotion ) use ( $format ) {
|
||||
return isset( $promotion['format'] ) && $format === $promotion['format'];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find promotions that are still active – they have a date range that
|
||||
* includes the current date.
|
||||
*
|
||||
* @param ?array $promotions Data about current promotions.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function get_active_promotions( $promotions = array() ) {
|
||||
$now_date_time = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
|
||||
$active_promotions = array();
|
||||
|
||||
foreach ( $promotions as $promotion ) {
|
||||
if ( ! isset( $promotion['date_from_gmt'] ) || ! isset( $promotion['date_to_gmt'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$date_from_gmt = new DateTime( $promotion['date_from_gmt'], new DateTimeZone( 'UTC' ) );
|
||||
$date_to_gmt = new DateTime( $promotion['date_to_gmt'], new DateTimeZone( 'UTC' ) );
|
||||
} catch ( \Exception $ex ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $now_date_time >= $date_from_gmt && $now_date_time <= $date_to_gmt ) {
|
||||
$active_promotions[] = $promotion;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort promotions so the ones starting more recently are at the top.
|
||||
usort(
|
||||
$active_promotions,
|
||||
function ( $a, $b ) {
|
||||
return $b['date_from_gmt'] <=> $a['date_from_gmt'];
|
||||
}
|
||||
);
|
||||
|
||||
return $active_promotions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for the `woocommerce_marketplace_menu_items` filter
|
||||
* in `Automattic\WooCommerce\Internal\Admin\Marketplace::get_marketplace_pages`.
|
||||
* At the moment, the Extensions page is the only page in `$menu_items`.
|
||||
* Adds a bubble to the menu item.
|
||||
*
|
||||
* @param array $menu_items Arrays representing items in nav menu.
|
||||
* @param ?array $promotion Data about a promotion from the Woo.com API.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function filter_marketplace_menu_items( $menu_items, $promotion = array() ) {
|
||||
if ( ! isset( $promotion['menu_item_id'] ) || ! isset( $promotion['content'] ) ) {
|
||||
return $menu_items;
|
||||
}
|
||||
foreach ( $menu_items as $index => $menu_item ) {
|
||||
if (
|
||||
'woocommerce' === $menu_item['parent']
|
||||
&& $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 );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $menu_items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the markup for a menu item bubble with a given text.
|
||||
*
|
||||
* @param string $bubble_text Text of bubble.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function append_bubble( $bubble_text ) {
|
||||
return ' <span class="awaiting-mod update-plugins remaining-tasks-badge woocommerce-task-list-remaining-tasks-badge">' . esc_html( $bubble_text ) . '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* When WooCommerce is deactivated, clear the scheduled action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clear_scheduled_event() {
|
||||
$timestamp = wp_next_scheduled( self::SCHEDULED_ACTION_HOOK );
|
||||
wp_unschedule_event( $timestamp, self::SCHEDULED_ACTION_HOOK );
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,7 @@ class WC_Admin_Meta_Boxes {
|
||||
* @return string[] Templates array excluding block-based templates.
|
||||
*/
|
||||
public function remove_block_templates( $templates ) {
|
||||
if ( count( $templates ) === 0 || ! wc_current_theme_is_fse_theme() || ( ! function_exists( 'gutenberg_get_block_template' ) && ! function_exists( 'get_block_template' ) ) ) {
|
||||
if ( count( $templates ) === 0 || ! wc_current_theme_is_fse_theme() ) {
|
||||
return $templates;
|
||||
}
|
||||
|
||||
@@ -296,9 +296,7 @@ class WC_Admin_Meta_Boxes {
|
||||
continue;
|
||||
}
|
||||
|
||||
$block_template = function_exists( 'gutenberg_get_block_template' ) ?
|
||||
gutenberg_get_block_template( $theme . '//' . $template_key ) :
|
||||
get_block_template( $theme . '//' . $template_key );
|
||||
$block_template = get_block_template( $theme . '//' . $template_key );
|
||||
|
||||
// If the block template has the product post type specified, include it.
|
||||
if ( $block_template && is_array( $block_template->post_types ) && in_array( 'product', $block_template->post_types ) ) {
|
||||
|
||||
@@ -305,6 +305,8 @@ class WC_Admin_Notices {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once WC_ABSPATH . 'includes/admin/wc-admin-functions.php';
|
||||
|
||||
$screen = get_current_screen();
|
||||
$screen_id = $screen ? $screen->id : '';
|
||||
$show_on_screens = array(
|
||||
|
||||
@@ -192,7 +192,7 @@ class WC_Admin_Webhooks {
|
||||
$webhook_id = absint( $_GET['delete'] );
|
||||
|
||||
if ( $webhook_id ) {
|
||||
$this->bulk_delete( array( $webhook_id ) );
|
||||
self::bulk_delete( array( $webhook_id ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,9 @@ class WC_Admin {
|
||||
if ( isset( $_GET['page'] ) && 'wc-addons' === $_GET['page'] ) {
|
||||
add_filter( 'admin_body_class', array( 'WC_Admin_Addons', 'filter_admin_body_classes' ) );
|
||||
}
|
||||
|
||||
// Fetch list of promotions from Woo.com for WooCommerce admin UI. We need to fire earlier than admin_init so we can filter menu items.
|
||||
add_action( 'woocommerce_init', array( 'WC_Admin_Marketplace_Promotions', 'init_marketplace_promotions' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,6 +80,9 @@ class WC_Admin {
|
||||
// Marketplace suggestions & related REST API.
|
||||
include_once __DIR__ . '/marketplace-suggestions/class-wc-marketplace-suggestions.php';
|
||||
include_once __DIR__ . '/marketplace-suggestions/class-wc-marketplace-updater.php';
|
||||
|
||||
// Marketplace promotions.
|
||||
include_once __DIR__ . '/class-wc-admin-marketplace-promotions.php';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -408,7 +408,7 @@ class WC_Helper {
|
||||
}
|
||||
|
||||
$filters = array_fill_keys( array_keys( self::get_filters() ), 0 );
|
||||
if ( empty( $subscriptions ) ) {
|
||||
if ( ! is_array( $subscriptions ) || empty( $subscriptions ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -1461,7 +1461,7 @@ class WC_Helper {
|
||||
if ( is_readable( $txt ) ) {
|
||||
$txt = file_get_contents( $txt );
|
||||
$txt = preg_split( '#\s#', $txt );
|
||||
if ( count( $txt ) >= 2 ) {
|
||||
if ( is_array( $txt ) && count( $txt ) >= 2 ) {
|
||||
$header = sprintf( '%d:%s', $txt[0], $txt[1] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,8 +146,8 @@ class WC_Tax_Rate_Importer extends WP_Importer {
|
||||
if ( false !== $handle ) {
|
||||
|
||||
$header = fgetcsv( $handle, 0, $this->delimiter );
|
||||
|
||||
if ( 10 === count( $header ) ) {
|
||||
$count = is_countable( $header ) ? count( $header ) : 0;
|
||||
if ( 10 === $count ) {
|
||||
|
||||
$row = fgetcsv( $handle, 0, $this->delimiter );
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if ( count( $errors ) ) {
|
||||
if ( is_array( $errors ) && count( $errors ) ) {
|
||||
foreach ( $errors as $error ) {
|
||||
if ( ! is_wp_error( $error ) ) {
|
||||
continue;
|
||||
|
||||
@@ -136,8 +136,7 @@ class WC_Admin_List_Table_Coupons extends WC_Admin_List_Table {
|
||||
*/
|
||||
protected function render_products_column() {
|
||||
$product_ids = $this->object->get_product_ids();
|
||||
|
||||
if ( count( $product_ids ) > 0 ) {
|
||||
if ( is_array( $product_ids ) && count( $product_ids ) > 0 ) {
|
||||
echo esc_html( implode( ', ', $product_ids ) );
|
||||
} else {
|
||||
echo '–';
|
||||
|
||||
@@ -15,8 +15,7 @@ $arrow_img_url = WC_ADMIN_IMAGES_FOLDER_URL . '/product_data/no-variati
|
||||
?>
|
||||
<div id="variable_product_options" class="panel wc-metaboxes-wrapper hidden">
|
||||
<div id="variable_product_options_inner">
|
||||
|
||||
<?php if ( ! count( $variation_attributes ) ) : ?>
|
||||
<?php if ( ! isset( $variation_attributes ) || ! is_array( $variation_attributes ) || count( $variation_attributes ) === 0 ) : ?>
|
||||
|
||||
<div class="add-attributes-container">
|
||||
<div class="add-attributes-message">
|
||||
|
||||
@@ -343,7 +343,7 @@ class WC_Report_Sales_By_Category extends WC_Admin_Report {
|
||||
$index = 0;
|
||||
foreach ( $chart_data as $data ) {
|
||||
$color = isset( $this->chart_colours[ $index ] ) ? $this->chart_colours[ $index ] : $this->chart_colours[0];
|
||||
$width = $this->barwidth / sizeof( $chart_data );
|
||||
$width = $this->barwidth / count( $chart_data );
|
||||
$offset = ( $width * $index );
|
||||
$series = $data['data'];
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
|
||||
public function output_tax_rates() {
|
||||
global $current_section;
|
||||
|
||||
$current_class = $this->get_current_tax_class();
|
||||
$current_class = self::get_current_tax_class();
|
||||
|
||||
$countries = array();
|
||||
foreach ( WC()->countries->get_allowed_countries() as $value => $label ) {
|
||||
@@ -320,7 +320,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing -- this is called via "do_action('woocommerce_settings_save_'...") in base class, where nonce is verified first.
|
||||
global $wpdb;
|
||||
|
||||
$current_class = sanitize_title( $this->get_current_tax_class() );
|
||||
$current_class = sanitize_title( self::get_current_tax_class() );
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Missing
|
||||
$posted_countries = wc_clean( wp_unslash( $_POST['tax_rate_country'] ) );
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
<div>
|
||||
<button id="btn-back" class="button button-large wc-backbone-modal-back-{{ data.status === 'new' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Back', 'woocommerce' ); ?></button>
|
||||
<button id="btn-ok" data-status='{{ data.status }}' class="button button-primary button-large">
|
||||
<div class="wc-backbone-modal-action-{{ data.status === 'new' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Create', 'woocommerce' ); ?></div>
|
||||
<div class="wc-backbone-modal-action-{{ data.status === 'new' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Create and save', 'woocommerce' ); ?></div>
|
||||
<div class="wc-backbone-modal-action-{{ data.status === 'existing' ? 'active' : 'inactive' }}"><?php esc_html_e( 'Save', 'woocommerce' ); ?></div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -21,9 +21,14 @@ $dropins_mu_plugins = $report['dropins_mu_plugins'];
|
||||
$theme = $report['theme'];
|
||||
$security = $report['security'];
|
||||
$settings = $report['settings'];
|
||||
$logging = $report['logging'];
|
||||
$wp_pages = $report['pages'];
|
||||
$plugin_updates = new WC_Plugin_Updates();
|
||||
$untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, Constants::get_constant( 'WC_SSR_PLUGIN_UPDATE_RELEASE_VERSION_TYPE' ) );
|
||||
|
||||
$active_plugins_count = is_countable( $active_plugins ) ? count( $active_plugins ) : 0;
|
||||
$inactive_plugins_count = is_countable( $inactive_plugins ) ? count( $inactive_plugins ) : 0;
|
||||
|
||||
?>
|
||||
<div class="updated woocommerce-message inline">
|
||||
<p>
|
||||
@@ -87,26 +92,6 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, Cons
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-export-label="WC Blocks Version"><?php esc_html_e( 'WooCommerce Blocks package', 'woocommerce' ); ?>:</td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'The WooCommerce Blocks package running on your site.', 'woocommerce' ) ); ?></td>
|
||||
<td>
|
||||
<?php
|
||||
if ( class_exists( '\Automattic\WooCommerce\Blocks\Package' ) ) {
|
||||
$version = \Automattic\WooCommerce\Blocks\Package::get_version();
|
||||
$path = \Automattic\WooCommerce\Blocks\Package::get_path(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
} else {
|
||||
$version = null;
|
||||
}
|
||||
|
||||
if ( ! is_null( $version ) ) {
|
||||
echo '<mark class="yes"><span class="dashicons dashicons-yes"></span> ' . esc_html( $version ) . ' <code class="private">' . esc_html( $path ) . '</code></mark> ';
|
||||
} else {
|
||||
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . esc_html__( 'Unable to detect the Blocks package.', 'woocommerce' ) . '</mark>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-export-label="Action Scheduler Version"><?php esc_html_e( 'Action Scheduler package', 'woocommerce' ); ?>:</td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'Action Scheduler package running on your site.', 'woocommerce' ) ); ?></td>
|
||||
@@ -594,7 +579,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, Cons
|
||||
<table class="wc_status_table widefat" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" data-export-label="Active Plugins (<?php echo count( $active_plugins ); ?>)"><h2><?php esc_html_e( 'Active plugins', 'woocommerce' ); ?> (<?php echo count( $active_plugins ); ?>)</h2></th>
|
||||
<th colspan="3" data-export-label="Active Plugins (<?php echo esc_attr( $active_plugins_count ); ?>)"><h2><?php esc_html_e( 'Active plugins', 'woocommerce' ); ?> (<?php echo esc_attr( $active_plugins_count ); ?>)</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -604,7 +589,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, Cons
|
||||
<table class="wc_status_table widefat" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" data-export-label="Inactive Plugins (<?php echo count( $inactive_plugins ); ?>)"><h2><?php esc_html_e( 'Inactive plugins', 'woocommerce' ); ?> (<?php echo count( $inactive_plugins ); ?>)</h2></th>
|
||||
<th colspan="3" data-export-label="Inactive Plugins (<?php echo esc_attr( $inactive_plugins_count ); ?>)"><h2><?php esc_html_e( 'Inactive plugins', 'woocommerce' ); ?> (<?php echo esc_attr( $inactive_plugins_count ); ?>)</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -612,12 +597,13 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, Cons
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
if ( 0 < count( $dropins_mu_plugins['dropins'] ) ) :
|
||||
$dropins_count = is_countable( $dropins_mu_plugins['dropins'] ) ? count( $dropins_mu_plugins['dropins'] ) : 0;
|
||||
if ( 0 < $dropins_count ) :
|
||||
?>
|
||||
<table class="wc_status_table widefat" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" data-export-label="Dropin Plugins (<?php echo count( $dropins_mu_plugins['dropins'] ); ?>)"><h2><?php esc_html_e( 'Dropin Plugins', 'woocommerce' ); ?> (<?php echo count( $dropins_mu_plugins['dropins'] ); ?>)</h2></th>
|
||||
<th colspan="3" data-export-label="Dropin Plugins (<?php $dropins_count; ?>)"><h2><?php esc_html_e( 'Dropin Plugins', 'woocommerce' ); ?> (<?php $dropins_count; ?>)</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -636,12 +622,14 @@ if ( 0 < count( $dropins_mu_plugins['dropins'] ) ) :
|
||||
</table>
|
||||
<?php
|
||||
endif;
|
||||
if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
||||
|
||||
$mu_plugins_count = is_countable( $dropins_mu_plugins['mu_plugins'] ) ? count( $dropins_mu_plugins['mu_plugins'] ) : 0;
|
||||
if ( 0 < $mu_plugins_count ) :
|
||||
?>
|
||||
<table class="wc_status_table widefat" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" data-export-label="Must Use Plugins (<?php echo count( $dropins_mu_plugins['mu_plugins'] ); ?>)"><h2><?php esc_html_e( 'Must Use Plugins', 'woocommerce' ); ?> (<?php echo count( $dropins_mu_plugins['mu_plugins'] ); ?>)</h2></th>
|
||||
<th colspan="3" data-export-label="Must Use Plugins (<?php echo esc_attr( $mu_plugins_count ); ?>)"><h2><?php esc_html_e( 'Must Use Plugins', 'woocommerce' ); ?> (<?php echo esc_attr( $mu_plugins_count ); ?>)</h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -770,6 +758,55 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="wc_status_table widefat" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" data-export-label="Logging"><h2><?php esc_html_e( 'Logging', 'woocommerce' ); ?></h2></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td data-export-label="Enabled"><?php esc_html_e( 'Enabled', 'woocommerce' ); ?></td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'Is logging enabled?', 'woocommerce' ) ); ?></td>
|
||||
<td><?php echo $logging['logging_enabled'] ? '<mark class="yes"><span class="dashicons dashicons-yes"></span></mark>' : '<mark class="no">–</mark>'; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-export-label="Handler"><?php esc_html_e( 'Handler', 'woocommerce' ); ?></td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'How log entries are being stored.', 'woocommerce' ) ); ?></td>
|
||||
<td><?php echo esc_html( $logging['default_handler'] ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-export-label="Retention period"><?php esc_html_e( 'Retention period', 'woocommerce' ); ?></td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'How many days log entries will be kept before being auto-deleted.', 'woocommerce' ) ); ?></td>
|
||||
<td>
|
||||
<?php
|
||||
printf(
|
||||
esc_html(
|
||||
// translators: %s is a number of days.
|
||||
_n(
|
||||
'%s day',
|
||||
'%s days',
|
||||
$logging['retention_period_days'],
|
||||
'woocommerce'
|
||||
)
|
||||
),
|
||||
esc_html( number_format_i18n( $logging['retention_period_days'] ) )
|
||||
);
|
||||
?>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-export-label="Level threshold"><?php esc_html_e( 'Level threshold', 'woocommerce' ); ?></td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'The minimum severity level of logs that will be stored.', 'woocommerce' ) ); ?></td>
|
||||
<td><?php echo $logging['level_threshold'] ? esc_html( $logging['level_threshold'] ) : '<mark class="no">–</mark>'; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-export-label="Log directory size"><?php esc_html_e( 'Log directory size', 'woocommerce' ); ?></td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'The total size of the files in the log directory.', 'woocommerce' ) ); ?></td>
|
||||
<td><?php echo esc_html( $logging['log_directory_size'] ); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="wc_status_table widefat" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -929,7 +966,7 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
||||
<td class="help"> </td>
|
||||
<td>
|
||||
<?php
|
||||
$total_overrides = count( $theme['overrides'] );
|
||||
$total_overrides = is_countable( $theme['overrides'] ) ? count( $theme['overrides'] ) : 0;
|
||||
for ( $i = 0; $i < $total_overrides; $i++ ) {
|
||||
$override = $theme['overrides'][ $i ];
|
||||
if ( $override['core_version'] && ( empty( $override['version'] ) || version_compare( $override['version'], $override['core_version'], '<' ) ) ) {
|
||||
@@ -944,7 +981,8 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
||||
} else {
|
||||
echo esc_html( $override['file'] );
|
||||
}
|
||||
if ( ( count( $theme['overrides'] ) - 1 ) !== $i ) {
|
||||
|
||||
if ( ( $total_overrides - 1 ) !== $i ) {
|
||||
echo ', ';
|
||||
}
|
||||
echo '<br />';
|
||||
|
||||
@@ -45,6 +45,10 @@ foreach ( $tools as $action_name => $tool ) {
|
||||
</p>
|
||||
</th>
|
||||
<td class="run-tool">
|
||||
<?php if ( ! empty( $tool['status_text'] ) ) : ?>
|
||||
<span class="run-tool-status"><?php echo wp_kses_post( $tool['status_text'] ); ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<input <?php echo ArrayUtil::is_truthy( $tool, 'disabled' ) ? 'disabled' : ''; ?> type="submit" form="<?php echo 'form_' . $action_name; ?>" class="button button-large" value="<?php echo esc_attr( $tool['button'] ); ?>" />
|
||||
</td>
|
||||
|
||||
@@ -288,6 +288,9 @@ class WC_AJAX {
|
||||
|
||||
if ( is_array( $posted_shipping_methods ) ) {
|
||||
foreach ( $posted_shipping_methods as $i => $value ) {
|
||||
if ( ! is_string( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
$chosen_shipping_methods[ $i ] = $value;
|
||||
}
|
||||
}
|
||||
@@ -347,6 +350,9 @@ class WC_AJAX {
|
||||
|
||||
if ( is_array( $posted_shipping_methods ) ) {
|
||||
foreach ( $posted_shipping_methods as $i => $value ) {
|
||||
if ( ! is_string( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
$chosen_shipping_methods[ $i ] = $value;
|
||||
}
|
||||
}
|
||||
@@ -2058,7 +2064,8 @@ class WC_AJAX {
|
||||
|
||||
$children = get_terms( $taxonomy, "child_of=$id&menu_order=ASC&hide_empty=0" );
|
||||
|
||||
if ( $term && count( $children ) ) {
|
||||
$children_count = is_countable( $children ) ? count( $children ) : 0;
|
||||
if ( $term && $children_count ) {
|
||||
echo 'children';
|
||||
wp_die();
|
||||
}
|
||||
|
||||
@@ -695,10 +695,8 @@ class WC_Checkout {
|
||||
)
|
||||
);
|
||||
|
||||
// Avoid storing used_by - it's not needed and can get large.
|
||||
$coupon_data = $coupon->get_data();
|
||||
unset( $coupon_data['used_by'] );
|
||||
$item->add_meta_data( 'coupon_data', $coupon_data );
|
||||
$coupon_info = $coupon->get_short_info();
|
||||
$item->add_meta_data( 'coupon_info', $coupon_info );
|
||||
|
||||
/**
|
||||
* Action hook to adjust item before save.
|
||||
@@ -751,7 +749,7 @@ class WC_Checkout {
|
||||
);
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Missing
|
||||
|
||||
$skipped = array();
|
||||
$skipped = array();
|
||||
$form_was_shown = isset( $_POST['woocommerce-process-checkout-nonce'] ); // phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||
|
||||
foreach ( $this->get_checkout_fields() as $fieldset_key => $fieldset ) {
|
||||
@@ -783,6 +781,9 @@ class WC_Checkout {
|
||||
$value = wc_sanitize_textarea( $value );
|
||||
break;
|
||||
case 'password':
|
||||
if ( $data['createaccount'] && 'account_password' === $key ) {
|
||||
$value = wp_slash( $value ); // Passwords are encrypted with slashes on account creation, so we need to slash here too.
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$value = wc_clean( $value );
|
||||
@@ -1020,6 +1021,9 @@ class WC_Checkout {
|
||||
|
||||
if ( is_array( $data['shipping_method'] ) ) {
|
||||
foreach ( $data['shipping_method'] as $i => $value ) {
|
||||
if ( ! is_string( $value ) ) {
|
||||
continue;
|
||||
}
|
||||
$chosen_shipping_methods[ $i ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,34 +287,34 @@ class WC_Countries {
|
||||
* @return array
|
||||
*/
|
||||
public function get_allowed_countries() {
|
||||
if ( 'all' === get_option( 'woocommerce_allowed_countries' ) ) {
|
||||
return apply_filters( 'woocommerce_countries_allowed_countries', $this->countries );
|
||||
}
|
||||
$countries = $this->countries;
|
||||
$allowed_countries = get_option( 'woocommerce_allowed_countries' );
|
||||
|
||||
if ( 'all_except' === get_option( 'woocommerce_allowed_countries' ) ) {
|
||||
if ( 'all_except' === $allowed_countries ) {
|
||||
$except_countries = get_option( 'woocommerce_all_except_countries', array() );
|
||||
|
||||
if ( ! $except_countries ) {
|
||||
return $this->countries;
|
||||
} else {
|
||||
$all_except_countries = $this->countries;
|
||||
if ( $except_countries ) {
|
||||
foreach ( $except_countries as $country ) {
|
||||
unset( $all_except_countries[ $country ] );
|
||||
unset( $countries[ $country ] );
|
||||
}
|
||||
}
|
||||
} elseif ( 'specific' === $allowed_countries ) {
|
||||
$countries = array();
|
||||
$raw_countries = get_option( 'woocommerce_specific_allowed_countries', array() );
|
||||
|
||||
if ( $raw_countries ) {
|
||||
foreach ( $raw_countries as $country ) {
|
||||
$countries[ $country ] = $this->countries[ $country ];
|
||||
}
|
||||
return apply_filters( 'woocommerce_countries_allowed_countries', $all_except_countries );
|
||||
}
|
||||
}
|
||||
|
||||
$countries = array();
|
||||
|
||||
$raw_countries = get_option( 'woocommerce_specific_allowed_countries', array() );
|
||||
|
||||
if ( $raw_countries ) {
|
||||
foreach ( $raw_countries as $country ) {
|
||||
$countries[ $country ] = $this->countries[ $country ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the list of allowed selling countries.
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @param array $countries
|
||||
*/
|
||||
return apply_filters( 'woocommerce_countries_allowed_countries', $countries );
|
||||
}
|
||||
|
||||
@@ -324,24 +324,34 @@ class WC_Countries {
|
||||
* @return array
|
||||
*/
|
||||
public function get_shipping_countries() {
|
||||
if ( '' === get_option( 'woocommerce_ship_to_countries' ) ) {
|
||||
return $this->get_allowed_countries();
|
||||
// If shipping is disabled, return an empty array.
|
||||
if ( 'disabled' === get_option( 'woocommerce_ship_to_countries' ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Default to selling countries.
|
||||
$countries = $this->get_allowed_countries();
|
||||
|
||||
// All indicates that all countries are allowed, regardless of where you sell to.
|
||||
if ( 'all' === get_option( 'woocommerce_ship_to_countries' ) ) {
|
||||
return $this->countries;
|
||||
}
|
||||
$countries = $this->countries;
|
||||
} elseif ( 'specific' === get_option( 'woocommerce_ship_to_countries' ) ) {
|
||||
$countries = array();
|
||||
$raw_countries = get_option( 'woocommerce_specific_ship_to_countries', array() );
|
||||
|
||||
$countries = array();
|
||||
|
||||
$raw_countries = get_option( 'woocommerce_specific_ship_to_countries' );
|
||||
|
||||
if ( $raw_countries ) {
|
||||
foreach ( $raw_countries as $country ) {
|
||||
$countries[ $country ] = $this->countries[ $country ];
|
||||
if ( $raw_countries ) {
|
||||
foreach ( $raw_countries as $country ) {
|
||||
$countries[ $country ] = $this->countries[ $country ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the list of allowed selling countries.
|
||||
*
|
||||
* @since 3.3.0
|
||||
* @param array $countries
|
||||
*/
|
||||
return apply_filters( 'woocommerce_countries_shipping_countries', $countries );
|
||||
}
|
||||
|
||||
@@ -859,7 +869,7 @@ class WC_Countries {
|
||||
),
|
||||
),
|
||||
'AL' => array(
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'County', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
@@ -968,7 +978,7 @@ class WC_Countries {
|
||||
'required' => false,
|
||||
'hidden' => true,
|
||||
),
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'required' => false,
|
||||
),
|
||||
),
|
||||
@@ -1011,7 +1021,7 @@ class WC_Countries {
|
||||
'postcode' => array(
|
||||
'required' => false,
|
||||
),
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Department', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
@@ -1054,12 +1064,12 @@ class WC_Countries {
|
||||
),
|
||||
),
|
||||
'DO' => array(
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Province', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'EC' => array(
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Province', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
@@ -1097,11 +1107,11 @@ class WC_Countries {
|
||||
),
|
||||
),
|
||||
'GG' => array(
|
||||
'state' => array(
|
||||
'required' => false,
|
||||
'label' => __( 'Parish', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'state' => array(
|
||||
'required' => false,
|
||||
'label' => __( 'Parish', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'GH' => array(
|
||||
'postcode' => array(
|
||||
'required' => false,
|
||||
@@ -1147,7 +1157,7 @@ class WC_Countries {
|
||||
),
|
||||
),
|
||||
'HN' => array(
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Department', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
@@ -1350,7 +1360,7 @@ class WC_Countries {
|
||||
),
|
||||
),
|
||||
'NI' => array(
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Department', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
@@ -1400,7 +1410,7 @@ class WC_Countries {
|
||||
),
|
||||
),
|
||||
'PA' => array(
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Province', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
@@ -1430,7 +1440,7 @@ class WC_Countries {
|
||||
),
|
||||
'PY' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Department', 'woocommerce' ),
|
||||
'label' => __( 'Department', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'RE' => array(
|
||||
@@ -1497,7 +1507,7 @@ class WC_Countries {
|
||||
),
|
||||
),
|
||||
'SV' => array(
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Department', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
@@ -1575,7 +1585,7 @@ class WC_Countries {
|
||||
),
|
||||
),
|
||||
'UY' => array(
|
||||
'state' => array(
|
||||
'state' => array(
|
||||
'label' => __( 'Department', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -81,6 +81,15 @@ class WC_Coupon extends WC_Legacy_Coupon {
|
||||
*/
|
||||
protected $cache_group = 'coupons';
|
||||
|
||||
/**
|
||||
* Sorting.
|
||||
*
|
||||
* Used by `get_coupons_from_cart` to sort coupons.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $sort = 0;
|
||||
|
||||
/**
|
||||
* Coupon constructor. Loads coupon data.
|
||||
*
|
||||
@@ -213,7 +222,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param string $context What the value is for. Valid values are 'view' and 'edit'.
|
||||
* @return float
|
||||
* @return string
|
||||
*/
|
||||
public function get_amount( $context = 'view' ) {
|
||||
return wc_format_decimal( $this->get_prop( 'amount', $context ) );
|
||||
@@ -1097,4 +1106,50 @@ class WC_Coupon extends WC_Legacy_Coupon {
|
||||
// When using this static method, there is no $this to pass to filter.
|
||||
return apply_filters( 'woocommerce_coupon_error', $err, $err_code, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the coupon information that is needed to reapply the coupon to an existing order.
|
||||
* This information is intended to be stored as a meta value in the order line item corresponding to the coupon
|
||||
* and should NOT be modified or extended (additional/custom data should go in a separate metadata entry).
|
||||
*
|
||||
* The information returned is a JSON-encoded string of an array with the following coupon information:
|
||||
*
|
||||
* 0: Id
|
||||
* 1: Code
|
||||
* 2: Type, null is equivalent to 'fixed_cart'
|
||||
* 3: Nominal amount (either a fixed amount or a percent, depending on the coupon type)
|
||||
* 4: The coupon grants free shipping? (present only if true)
|
||||
*
|
||||
* @return string A JSON string with information that allows the coupon to be reapplied to an existing order.
|
||||
*/
|
||||
public function get_short_info(): string {
|
||||
$type = $this->get_discount_type();
|
||||
$info = array(
|
||||
$this->get_id(),
|
||||
$this->get_code(),
|
||||
'fixed_cart' === $type ? null : $type,
|
||||
(float) $this->get_prop( 'amount' ),
|
||||
);
|
||||
|
||||
if ( $this->get_free_shipping() ) {
|
||||
$info[] = true;
|
||||
}
|
||||
|
||||
return wp_json_encode( $info );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the coupon parameters from a reapply information set generated with 'get_short_info'.
|
||||
*
|
||||
* @param string $info JSON string with reapply information as returned by 'get_short_info'.
|
||||
*/
|
||||
public function set_short_info( string $info ) {
|
||||
$info = json_decode( $info, true );
|
||||
|
||||
$this->set_id( $info[0] ?? 0 );
|
||||
$this->set_code( $info[1] ?? '' );
|
||||
$this->set_discount_type( $info[2] ?? 'fixed_cart' );
|
||||
$this->set_amount( $info[3] ?? 0 );
|
||||
$this->set_free_shipping( $info[4] ?? false );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,14 +175,6 @@ class WC_Deprecated_Action_Hooks extends WC_Deprecated_Hooks {
|
||||
do_action( $old_hook, $order_id, $item_id, $item, $item->get_product() );
|
||||
}
|
||||
break;
|
||||
case 'woocommerce_order_update_coupon':
|
||||
case 'woocommerce_order_update_shipping':
|
||||
case 'woocommerce_order_update_fee':
|
||||
case 'woocommerce_order_update_tax':
|
||||
if ( ! is_a( $item, 'WC_Order_Item_Product' ) ) {
|
||||
do_action( $old_hook, $order_id, $item_id, $item );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
do_action_ref_array( $old_hook, $new_callback_args );
|
||||
break;
|
||||
|
||||
@@ -353,12 +353,20 @@ class WC_Form_Handler {
|
||||
$customer->save();
|
||||
}
|
||||
|
||||
wc_add_notice( __( 'Account details changed successfully.', 'woocommerce' ) );
|
||||
|
||||
/**
|
||||
* Hook: woocommerce_save_account_details.
|
||||
*
|
||||
* @since 3.6.0
|
||||
* @param int $user_id User ID being saved.
|
||||
*/
|
||||
do_action( 'woocommerce_save_account_details', $user->ID );
|
||||
|
||||
wp_safe_redirect( wc_get_endpoint_url( 'edit-account', '', wc_get_page_permalink( 'myaccount' ) ) );
|
||||
exit;
|
||||
// Notices are checked here so that if something created a notice during the save hooks above, the redirect will not happen.
|
||||
if ( 0 === wc_notice_count( 'error' ) ) {
|
||||
wc_add_notice( __( 'Account details changed successfully.', 'woocommerce' ) );
|
||||
wp_safe_redirect( wc_get_endpoint_url( 'edit-account', '', wc_get_page_permalink( 'myaccount' ) ) );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,7 +909,7 @@ class WC_Form_Handler {
|
||||
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$variations = array();
|
||||
|
||||
$product = wc_get_product( $product_id );
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
foreach ( $_REQUEST as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( 'attribute_' !== substr( $key, 0, 10 ) ) {
|
||||
|
||||
@@ -1194,7 +1194,7 @@ class WC_Geo_IP {
|
||||
$this->memory_buffer = fread( $this->filehandle, $s_array['size'] );
|
||||
}
|
||||
} else {
|
||||
$this->log( 'GeoIP API: Can not open ' . $filename, 'error' );
|
||||
self::log( 'GeoIP API: Can not open ' . $filename, 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1553,7 +1553,7 @@ class WC_Geo_IP {
|
||||
}
|
||||
}
|
||||
|
||||
$this->log( 'GeoIP API: Error traversing database - perhaps it is corrupt?', 'error' );
|
||||
self::log( 'GeoIP API: Error traversing database - perhaps it is corrupt?', 'error' );
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1608,7 +1608,7 @@ class WC_Geo_IP {
|
||||
}
|
||||
}
|
||||
|
||||
$this->log( 'GeoIP API: Error traversing database - perhaps it is corrupt?', 'error' );
|
||||
self::log( 'GeoIP API: Error traversing database - perhaps it is corrupt?', 'error' );
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1637,7 +1637,7 @@ class WC_Geo_IP {
|
||||
*/
|
||||
public function geoip_country_id_by_addr_v6( $addr ) {
|
||||
if ( ! defined( 'AF_INET6' ) ) {
|
||||
$this->log( 'GEOIP (geoip_country_id_by_addr_v6): PHP was compiled with --disable-ipv6 option' );
|
||||
self::log( 'GEOIP (geoip_country_id_by_addr_v6): PHP was compiled with --disable-ipv6 option' );
|
||||
return false;
|
||||
}
|
||||
$ipnum = inet_pton( $addr );
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Admin\Notes\Notes;
|
||||
use Automattic\WooCommerce\Internal\TransientFiles\TransientFilesEngine;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\{ CustomOrdersTableController, DataSynchronizer, OrdersTableDataStore };
|
||||
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||
use Automattic\WooCommerce\Internal\ProductAttributesLookup\DataRegenerator;
|
||||
@@ -243,6 +244,9 @@ class WC_Install {
|
||||
'8.6.0' => array(
|
||||
'wc_update_860_remove_recommended_marketing_plugins_transient',
|
||||
),
|
||||
'8.7.0' => array(
|
||||
'wc_update_870_prevent_listing_of_transient_files_directory',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -456,6 +460,11 @@ class WC_Install {
|
||||
// plugin version update. We base plugin age off of this value.
|
||||
add_option( 'woocommerce_admin_install_timestamp', time() );
|
||||
|
||||
// Force a flush of rewrite rules even if the corresponding hook isn't initialized yet.
|
||||
if ( ! has_action( 'woocommerce_flush_rewrite_rules' ) ) {
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the rewrite rules after install or update.
|
||||
*
|
||||
@@ -557,6 +566,7 @@ class WC_Install {
|
||||
WC()->query->add_endpoints();
|
||||
WC_API::add_endpoint();
|
||||
WC_Auth::add_endpoint();
|
||||
TransientFilesEngine::add_endpoint();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,22 @@ defined( 'ABSPATH' ) || exit;
|
||||
*/
|
||||
class WC_Order_Item_Product extends WC_Order_Item {
|
||||
|
||||
/**
|
||||
* Legacy values.
|
||||
*
|
||||
* @deprecated 4.4.0 For legacy actions.
|
||||
* @var array
|
||||
*/
|
||||
public $legacy_values;
|
||||
|
||||
/**
|
||||
* Legacy cart item key.
|
||||
*
|
||||
* @deprecated 4.4.0 For legacy actions.
|
||||
* @var string
|
||||
*/
|
||||
public $legacy_cart_item_key;
|
||||
|
||||
/**
|
||||
* Order Data array. This is the core order data exposed in APIs since 3.0.0.
|
||||
*
|
||||
|
||||
@@ -52,6 +52,14 @@ class WC_Order_Item extends WC_Data implements ArrayAccess {
|
||||
*/
|
||||
protected $object_type = 'order_item';
|
||||
|
||||
/**
|
||||
* Legacy package key.
|
||||
*
|
||||
* @deprecated 4.4.0 For legacy actions.
|
||||
* @var string
|
||||
*/
|
||||
public $legacy_package_key;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -29,18 +29,18 @@ class WC_Product_Factory {
|
||||
return false;
|
||||
}
|
||||
|
||||
$product_type = $this->get_product_type( $product_id );
|
||||
$product_type = self::get_product_type( $product_id );
|
||||
|
||||
// Backwards compatibility.
|
||||
if ( ! empty( $deprecated ) ) {
|
||||
wc_deprecated_argument( 'args', '3.0', 'Passing args to the product factory is deprecated. If you need to force a type, construct the product class directly.' );
|
||||
|
||||
if ( isset( $deprecated['product_type'] ) ) {
|
||||
$product_type = $this->get_classname_from_product_type( $deprecated['product_type'] );
|
||||
$product_type = self::get_classname_from_product_type( $deprecated['product_type'] );
|
||||
}
|
||||
}
|
||||
|
||||
$classname = $this->get_product_classname( $product_id, $product_type );
|
||||
$classname = self::get_product_classname( $product_id, $product_type );
|
||||
|
||||
try {
|
||||
return new $classname( $product_id, $deprecated );
|
||||
|
||||
@@ -554,7 +554,7 @@ class WC_Query {
|
||||
*/
|
||||
private function product_query_post_clauses( $args, $wp_query ) {
|
||||
$args = $this->price_filter_post_clauses( $args, $wp_query );
|
||||
$args = $this->filterer->filter_by_attribute_post_clauses( $args, $wp_query, $this->get_layered_nav_chosen_attributes() );
|
||||
$args = $this->filterer->filter_by_attribute_post_clauses( $args, $wp_query, self::get_layered_nav_chosen_attributes() );
|
||||
|
||||
return $args;
|
||||
}
|
||||
@@ -790,7 +790,7 @@ class WC_Query {
|
||||
|
||||
if ( $main_query && ! $this->filterer->filtering_via_lookup_table_is_active() ) {
|
||||
// Layered nav filters on terms.
|
||||
foreach ( $this->get_layered_nav_chosen_attributes() as $taxonomy => $data ) {
|
||||
foreach ( self::get_layered_nav_chosen_attributes() as $taxonomy => $data ) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => $taxonomy,
|
||||
'field' => 'slug',
|
||||
|
||||
@@ -263,7 +263,7 @@ class WC_Regenerate_Images {
|
||||
private static function get_full_size_image_dimensions( $attachment_id ) {
|
||||
$imagedata = wp_get_attachment_metadata( $attachment_id );
|
||||
|
||||
if ( ! $imagedata ) {
|
||||
if ( ! is_array( $imagedata ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ class WC_Session_Handler extends WC_Session {
|
||||
$this->init_session_cookie();
|
||||
|
||||
add_action( 'woocommerce_set_cart_cookies', array( $this, 'set_customer_session_cookie' ), 10 );
|
||||
add_action( 'wp', array( $this, 'maybe_set_customer_session_cookie' ), 99 );
|
||||
add_action( 'shutdown', array( $this, 'save_data' ), 20 );
|
||||
add_action( 'wp_logout', array( $this, 'destroy_session' ) );
|
||||
|
||||
@@ -137,7 +138,7 @@ class WC_Session_Handler extends WC_Session {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Session from a different user is not valid. (Although from a guest user will be valid)
|
||||
// Session from a different user is not valid. (Although from a guest user will be valid).
|
||||
if ( is_user_logged_in() && ! $this->is_customer_guest( $this->_customer_id ) && strval( get_current_user_id() ) !== $this->_customer_id ) {
|
||||
return false;
|
||||
}
|
||||
@@ -145,6 +146,18 @@ class WC_Session_Handler extends WC_Session {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks into the wp action to maybe set the session cookie if the user is on a certain page e.g. a checkout endpoint.
|
||||
*
|
||||
* Certain gateways may rely on sessions and this ensures a session is present even if the customer does not have a
|
||||
* cart.
|
||||
*/
|
||||
public function maybe_set_customer_session_cookie() {
|
||||
if ( is_wc_endpoint_url( 'order-pay' ) ) {
|
||||
$this->set_customer_session_cookie( true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session cookie on-demand (usually after adding an item to the cart).
|
||||
*
|
||||
@@ -345,7 +358,8 @@ class WC_Session_Handler extends WC_Session {
|
||||
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"INSERT INTO {$wpdb->prefix}woocommerce_sessions (`session_key`, `session_value`, `session_expiry`) VALUES (%s, %s, %d)
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
"INSERT INTO $this->_table (`session_key`, `session_value`, `session_expiry`) VALUES (%s, %s, %d)
|
||||
ON DUPLICATE KEY UPDATE `session_value` = VALUES(`session_value`), `session_expiry` = VALUES(`session_expiry`)",
|
||||
$this->_customer_id,
|
||||
maybe_serialize( $this->_data ),
|
||||
|
||||
@@ -241,6 +241,36 @@ class WC_Structured_Data {
|
||||
'offerCount' => count( $product->get_children() ),
|
||||
);
|
||||
}
|
||||
} elseif ( $product->is_type( 'grouped' ) ) {
|
||||
if ( $product->is_on_sale() && $product->get_date_on_sale_to() ) {
|
||||
$price_valid_until = gmdate( 'Y-m-d', $product->get_date_on_sale_to()->getTimestamp() );
|
||||
}
|
||||
|
||||
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
|
||||
$children = array_filter( array_map( 'wc_get_product', $product->get_children() ), 'wc_products_array_filter_visible_grouped' );
|
||||
$price_function = 'incl' === $tax_display_mode ? 'wc_get_price_including_tax' : 'wc_get_price_excluding_tax';
|
||||
|
||||
foreach ( $children as $child ) {
|
||||
if ( '' !== $child->get_price() ) {
|
||||
$child_prices[] = $price_function( $child );
|
||||
}
|
||||
}
|
||||
if ( empty( $child_prices ) ) {
|
||||
$min_price = 0;
|
||||
} else {
|
||||
$min_price = min( $child_prices );
|
||||
}
|
||||
|
||||
$markup_offer = array(
|
||||
'@type' => 'Offer',
|
||||
'price' => wc_format_decimal( $min_price, wc_get_price_decimals() ),
|
||||
'priceValidUntil' => $price_valid_until,
|
||||
'priceSpecification' => array(
|
||||
'price' => wc_format_decimal( $min_price, wc_get_price_decimals() ),
|
||||
'priceCurrency' => $currency,
|
||||
'valueAddedTaxIncluded' => wc_prices_include_tax() ? 'true' : 'false',
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if ( $product->is_on_sale() && $product->get_date_on_sale_to() ) {
|
||||
$price_valid_until = gmdate( 'Y-m-d', $product->get_date_on_sale_to()->getTimestamp() );
|
||||
|
||||
@@ -625,8 +625,8 @@ class WC_Tracker {
|
||||
$curr_tokens = preg_split( '/[ :,\-_]+/', $key );
|
||||
$prev_tokens = preg_split( '/[ :,\-_]+/', $prev );
|
||||
|
||||
$len_curr = count( $curr_tokens );
|
||||
$len_prev = count( $prev_tokens );
|
||||
$len_curr = is_array( $curr_tokens ) ? count( $curr_tokens ) : 0;
|
||||
$len_prev = is_array( $prev_tokens ) ? count( $prev_tokens ) : 0;
|
||||
|
||||
$index_unique = -1;
|
||||
// Gather the common tokens.
|
||||
@@ -640,7 +640,7 @@ class WC_Tracker {
|
||||
}
|
||||
|
||||
// If only one token is different, and those tokens contain digits, then that could be the unique id.
|
||||
if ( count( $curr_tokens ) - count( $comm_tokens ) <= 1 && count( $comm_tokens ) > 0 && $index_unique > -1 ) {
|
||||
if ( $len_curr - count( $comm_tokens ) <= 1 && count( $comm_tokens ) > 0 && $index_unique > -1 ) {
|
||||
$objects[ $key ]->group_key = implode( ' ', $comm_tokens );
|
||||
$objects[ $prev ]->group_key = implode( ' ', $comm_tokens );
|
||||
} else {
|
||||
@@ -1072,8 +1072,9 @@ class WC_Tracker {
|
||||
* - pickup_locations_count
|
||||
*/
|
||||
public static function get_pickup_location_data() {
|
||||
$pickup_location_enabled = false;
|
||||
$pickup_locations_count = count( get_option( 'pickup_location_pickup_locations', array() ) );
|
||||
$pickup_location_enabled = false;
|
||||
$pickup_location_pickup_locations = get_option( 'pickup_location_pickup_locations', array() );
|
||||
$pickup_locations_count = is_countable( $pickup_location_pickup_locations ) ? count( $pickup_location_pickup_locations ) : 0;
|
||||
|
||||
// Get the available shipping methods.
|
||||
$shipping_methods = WC()->shipping()->get_shipping_methods();
|
||||
|
||||
@@ -37,7 +37,7 @@ final class WooCommerce {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = '8.6.1';
|
||||
public $version = '8.7.0';
|
||||
|
||||
/**
|
||||
* WooCommerce Schema version.
|
||||
@@ -250,6 +250,8 @@ final class WooCommerce {
|
||||
add_action( 'deactivated_plugin', array( $this, 'deactivated_plugin' ) );
|
||||
add_action( 'woocommerce_installed', array( $this, 'add_woocommerce_inbox_variant' ) );
|
||||
add_action( 'woocommerce_updated', array( $this, 'add_woocommerce_inbox_variant' ) );
|
||||
add_action( 'woocommerce_installed', array( $this, 'add_woocommerce_remote_variant' ) );
|
||||
add_action( 'woocommerce_updated', array( $this, 'add_woocommerce_remote_variant' ) );
|
||||
|
||||
// These classes set up hooks on instantiation.
|
||||
$container = wc_get_container();
|
||||
@@ -283,6 +285,9 @@ final class WooCommerce {
|
||||
* Add woocommerce_inbox_variant for the Remote Inbox Notification.
|
||||
*
|
||||
* P2 post can be found at https://wp.me/paJDYF-1uJ.
|
||||
*
|
||||
* This will no longer be used. The more flexible add_woocommerce_remote_variant
|
||||
* below will be used instead.
|
||||
*/
|
||||
public function add_woocommerce_inbox_variant() {
|
||||
$config_name = 'woocommerce_inbox_variant_assignment';
|
||||
@@ -290,6 +295,18 @@ final class WooCommerce {
|
||||
update_option( $config_name, wp_rand( 1, 12 ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add woocommerce_remote_variant_assignment used to determine cohort
|
||||
* or group assignment for Remote Spec Engines.
|
||||
*/
|
||||
public function add_woocommerce_remote_variant() {
|
||||
$config_name = 'woocommerce_remote_variant_assignment';
|
||||
if ( false === get_option( $config_name, false ) ) {
|
||||
update_option( $config_name, wp_rand( 1, 120 ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures fatal errors are logged so they can be picked up in the status report.
|
||||
*
|
||||
@@ -910,14 +927,15 @@ final class WooCommerce {
|
||||
/**
|
||||
* Initialize the customer and cart objects and setup customer saving on shutdown.
|
||||
*
|
||||
* Note, wc()->customer is session based. Changes to customer data via this property are not persisted to the database automatically.
|
||||
*
|
||||
* @since 3.6.4
|
||||
* @return void
|
||||
*/
|
||||
public function initialize_cart() {
|
||||
// Cart needs customer info.
|
||||
if ( is_null( $this->customer ) || ! $this->customer instanceof WC_Customer ) {
|
||||
$this->customer = new WC_Customer( get_current_user_id(), true );
|
||||
// Customer should be saved during shutdown.
|
||||
// Customer session should be saved during shutdown.
|
||||
add_action( 'shutdown', array( $this->customer, 'save' ), 10 );
|
||||
}
|
||||
if ( is_null( $this->cart ) || ! $this->cart instanceof WC_Cart ) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
@@ -707,6 +708,8 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
* @param WC_Abstract_Order $order Order object.
|
||||
*/
|
||||
protected function update_order_meta_from_object( $order ) {
|
||||
global $wpdb;
|
||||
|
||||
if ( is_null( $order->get_meta() ) ) {
|
||||
return;
|
||||
}
|
||||
@@ -734,7 +737,23 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
}
|
||||
}
|
||||
}
|
||||
add_post_meta( $order->get_id(), $meta_data->key, $meta_data->value, false );
|
||||
if ( is_object( $meta_data->value ) && '__PHP_Incomplete_Class' === get_class( $meta_data->value ) ) {
|
||||
$meta_value = maybe_serialize( $meta_data->value );
|
||||
$result = $wpdb->insert(
|
||||
_get_meta_table( 'post' ),
|
||||
array(
|
||||
'post_id' => $order->get_id(),
|
||||
'meta_key' => $meta_data->key, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
'meta_value' => $meta_value, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||
),
|
||||
array( '%d', '%s', '%s' )
|
||||
);
|
||||
wp_cache_delete( $order->get_id(), 'post_meta' );
|
||||
$logger = wc_get_container()->get( LegacyProxy::class )->call_function( 'wc_get_logger' );
|
||||
$logger->warning( sprintf( 'encountered an order meta value of type __PHP_Incomplete_Class during `update_order_meta_from_object` in order with ID %d: "%s"', $order->get_id(), var_export( $meta_value, true ) ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
|
||||
} else {
|
||||
add_post_meta( $order->get_id(), $meta_data->key, $meta_data->value, false );
|
||||
}
|
||||
}
|
||||
|
||||
// Find remaining meta that was deleted from the order but still present in the associated post.
|
||||
|
||||
@@ -12,6 +12,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
/**
|
||||
* WC Customer Data Store which stores the data in session.
|
||||
*
|
||||
* Used by the WC_Customer class to store customer data to the session.
|
||||
*
|
||||
* @version 3.0.0
|
||||
*/
|
||||
class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Customer_Data_Store_Interface, WC_Object_Data_Store_Interface {
|
||||
@@ -24,35 +26,36 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust
|
||||
protected $session_keys = array(
|
||||
'id',
|
||||
'date_modified',
|
||||
'billing_postcode',
|
||||
'billing_city',
|
||||
'billing_address_1',
|
||||
'billing_address',
|
||||
'billing_address_2',
|
||||
'billing_state',
|
||||
'billing_country',
|
||||
'shipping_postcode',
|
||||
'shipping_city',
|
||||
'shipping_address_1',
|
||||
'shipping_address',
|
||||
'shipping_address_2',
|
||||
'shipping_state',
|
||||
'shipping_country',
|
||||
'is_vat_exempt',
|
||||
'calculated_shipping',
|
||||
'billing_first_name',
|
||||
'billing_last_name',
|
||||
'billing_company',
|
||||
'billing_phone',
|
||||
'billing_email',
|
||||
'billing_address',
|
||||
'billing_address_1',
|
||||
'billing_address_2',
|
||||
'billing_city',
|
||||
'billing_state',
|
||||
'billing_postcode',
|
||||
'billing_country',
|
||||
'shipping_first_name',
|
||||
'shipping_last_name',
|
||||
'shipping_company',
|
||||
'shipping_phone',
|
||||
'shipping_address',
|
||||
'shipping_address_1',
|
||||
'shipping_address_2',
|
||||
'shipping_city',
|
||||
'shipping_state',
|
||||
'shipping_postcode',
|
||||
'shipping_country',
|
||||
'is_vat_exempt',
|
||||
'calculated_shipping',
|
||||
'meta_data',
|
||||
);
|
||||
|
||||
/**
|
||||
* Simply update the session.
|
||||
* Update the session. Note, this does not persist the data to the DB.
|
||||
*
|
||||
* @param WC_Customer $customer Customer object.
|
||||
*/
|
||||
@@ -61,7 +64,7 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply update the session.
|
||||
* Update the session. Note, this does not persist the data to the DB.
|
||||
*
|
||||
* @param WC_Customer $customer Customer object.
|
||||
*/
|
||||
@@ -81,14 +84,36 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust
|
||||
if ( 'billing_' === substr( $session_key, 0, 8 ) ) {
|
||||
$session_key = str_replace( 'billing_', '', $session_key );
|
||||
}
|
||||
$data[ $session_key ] = (string) $customer->{"get_$function_key"}( 'edit' );
|
||||
if ( 'meta_data' === $session_key ) {
|
||||
/**
|
||||
* Filter the allowed session meta data keys.
|
||||
*
|
||||
* If the customer object contains any meta data with these keys, it will be stored within the WooCommerce session.
|
||||
*
|
||||
* @since 8.7.0
|
||||
* @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 );
|
||||
}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$session_value = $customer->{"get_$function_key"}( 'edit' );
|
||||
}
|
||||
$data[ $session_key ] = (string) $session_value;
|
||||
}
|
||||
WC()->session->set( 'customer', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Read customer data from the session unless the user has logged in, in
|
||||
* which case the stored ID will differ from the actual ID.
|
||||
* Read customer data from the session unless the user has logged in, in which case the stored ID will differ from
|
||||
* the actual ID.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param WC_Customer $customer Customer object.
|
||||
@@ -110,8 +135,20 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust
|
||||
if ( 'billing_' === substr( $session_key, 0, 8 ) ) {
|
||||
$session_key = str_replace( 'billing_', '', $session_key );
|
||||
}
|
||||
if ( isset( $data[ $session_key ] ) && is_callable( array( $customer, "set_{$function_key}" ) ) ) {
|
||||
$customer->{"set_{$function_key}"}( wp_unslash( $data[ $session_key ] ) );
|
||||
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 ( ! isset( $meta_data_value['key'], $meta_data_value['value'] ) ) {
|
||||
continue;
|
||||
}
|
||||
$customer->add_meta_data( $meta_data_value['key'], $meta_data_value['value'], true );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$customer->{"set_{$function_key}"}( wp_unslash( $data[ $session_key ] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,13 +157,13 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust
|
||||
}
|
||||
|
||||
/**
|
||||
* Load default values if props are unset.
|
||||
* Set default values for the customer object if props are unset.
|
||||
*
|
||||
* @param WC_Customer $customer Customer object.
|
||||
*/
|
||||
protected function set_defaults( &$customer ) {
|
||||
try {
|
||||
$default = wc_get_customer_default_location();
|
||||
$default = wc_get_customer_default_location();
|
||||
$has_shipping_address = $customer->has_shipping_address();
|
||||
|
||||
if ( ! $customer->get_billing_country() ) {
|
||||
@@ -154,7 +191,7 @@ class WC_Customer_Data_Store_Session extends WC_Data_Store_WP implements WC_Cust
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a customer from the database.
|
||||
* Deletes the customer session.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param WC_Customer $customer Customer object.
|
||||
|
||||
@@ -89,6 +89,7 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
||||
$order = wc_get_order( $order_id );
|
||||
}
|
||||
|
||||
$email_already_sent = false;
|
||||
if ( is_a( $order, 'WC_Order' ) ) {
|
||||
$this->object = $order;
|
||||
$this->placeholders['{order_date}'] = wc_format_datetime( $this->object->get_date_created() );
|
||||
|
||||
@@ -236,6 +236,7 @@ class WC_Gateway_COD extends WC_Payment_Gateway {
|
||||
|
||||
$data_store = WC_Data_Store::load( 'shipping-zone' );
|
||||
$raw_zones = $data_store->get_zones();
|
||||
$zones = array();
|
||||
|
||||
foreach ( $raw_zones as $raw_zone ) {
|
||||
$zones[] = new WC_Shipping_Zone( $raw_zone );
|
||||
@@ -327,7 +328,7 @@ class WC_Gateway_COD extends WC_Payment_Gateway {
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param array $rate_ids Rate ids to check.
|
||||
* @return boolean
|
||||
* @return array
|
||||
*/
|
||||
private function get_matching_rates( $rate_ids ) {
|
||||
// First, match entries in 'method_id:instance_id' format. Then, match entries in 'method_id' format by stripping off the instance ID from the candidates.
|
||||
|
||||
@@ -418,11 +418,11 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
||||
$result = WC_Gateway_Paypal_API_Handler::refund_transaction( $order, $amount, $reason );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$this->log( 'Refund Failed: ' . $result->get_error_message(), 'error' );
|
||||
static::log( 'Refund Failed: ' . $result->get_error_message(), 'error' );
|
||||
return new WP_Error( 'error', $result->get_error_message() );
|
||||
}
|
||||
|
||||
$this->log( 'Refund Result: ' . wc_print_r( $result, true ) );
|
||||
static::log( 'Refund Result: ' . wc_print_r( $result, true ) );
|
||||
|
||||
switch ( strtolower( $result->ACK ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
case 'success':
|
||||
@@ -450,13 +450,13 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
||||
$result = WC_Gateway_Paypal_API_Handler::do_capture( $order );
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$this->log( 'Capture Failed: ' . $result->get_error_message(), 'error' );
|
||||
static::log( 'Capture Failed: ' . $result->get_error_message(), 'error' );
|
||||
/* translators: %s: Paypal gateway error message */
|
||||
$order->add_order_note( sprintf( __( 'Payment could not be captured: %s', 'woocommerce' ), $result->get_error_message() ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log( 'Capture Result: ' . wc_print_r( $result, true ) );
|
||||
static::log( 'Capture Result: ' . wc_print_r( $result, true ) );
|
||||
|
||||
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
|
||||
if ( ! empty( $result->PAYMENTSTATUS ) ) {
|
||||
|
||||
@@ -59,22 +59,22 @@ class WC_API_Customers extends WC_API_Resource {
|
||||
|
||||
# GET /customers
|
||||
$routes[ $this->base ] = array(
|
||||
array( array( $this, 'get_customers' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customers' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/count
|
||||
$routes[ $this->base . '/count' ] = array(
|
||||
array( array( $this, 'get_customers_count' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customers_count' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/<id>
|
||||
$routes[ $this->base . '/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_customer' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customer' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/<id>/orders
|
||||
$routes[ $this->base . '/(?P<id>\d+)/orders' ] = array(
|
||||
array( array( $this, 'get_customer_orders' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customer_orders' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
return $routes;
|
||||
|
||||
@@ -278,7 +278,7 @@ class WC_API_Server {
|
||||
|
||||
// Normalise the endpoints
|
||||
foreach ( $endpoints as $route => &$handlers ) {
|
||||
if ( count( $handlers ) <= 2 && isset( $handlers[1] ) && ! is_array( $handlers[1] ) ) {
|
||||
if ( is_array( $handlers ) && count( $handlers ) <= 2 && isset( $handlers[1] ) && ! is_array( $handlers[1] ) ) {
|
||||
$handlers = array( $handlers );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ class WC_API_Coupons extends WC_API_Resource {
|
||||
# GET/PUT/DELETE /coupons/<id>
|
||||
$routes[ $this->base . '/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_coupon' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_coupon' ), WC_API_SERVER::EDITABLE | WC_API_SERVER::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_coupon' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'edit_coupon' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_coupon' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# GET /coupons/code/<code>, note that coupon codes can contain spaces, dashes and underscores
|
||||
|
||||
@@ -58,35 +58,35 @@ class WC_API_Customers extends WC_API_Resource {
|
||||
|
||||
# GET/POST /customers
|
||||
$routes[ $this->base ] = array(
|
||||
array( array( $this, 'get_customers' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'create_customer' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'get_customers' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_customer' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET /customers/count
|
||||
$routes[ $this->base . '/count' ] = array(
|
||||
array( array( $this, 'get_customers_count' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customers_count' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET/PUT/DELETE /customers/<id>
|
||||
$routes[ $this->base . '/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_customer' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'edit_customer' ), WC_API_SERVER::EDITABLE | WC_API_SERVER::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_customer' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'get_customer' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_customer' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_customer' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/email/<email>
|
||||
$routes[ $this->base . '/email/(?P<email>.+)' ] = array(
|
||||
array( array( $this, 'get_customer_by_email' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customer_by_email' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/<id>/orders
|
||||
$routes[ $this->base . '/(?P<id>\d+)/orders' ] = array(
|
||||
array( array( $this, 'get_customer_orders' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customer_orders' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/<id>/downloads
|
||||
$routes[ $this->base . '/(?P<id>\d+)/downloads' ] = array(
|
||||
array( array( $this, 'get_customer_downloads' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customer_downloads' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# POST|PUT /customers/bulk
|
||||
|
||||
@@ -62,27 +62,27 @@ class WC_API_Orders extends WC_API_Resource {
|
||||
# GET|POST /orders/<id>/notes
|
||||
$routes[ $this->base . '/(?P<order_id>\d+)/notes' ] = array(
|
||||
array( array( $this, 'get_order_notes' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_order_note' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_order_note' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET|PUT|DELETE /orders/<order_id>/notes/<id>
|
||||
$routes[ $this->base . '/(?P<order_id>\d+)/notes/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_order_note' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_order_note' ), WC_API_SERVER::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_order_note' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'edit_order_note' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_order_note' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# GET|POST /orders/<order_id>/refunds
|
||||
$routes[ $this->base . '/(?P<order_id>\d+)/refunds' ] = array(
|
||||
array( array( $this, 'get_order_refunds' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_order_refund' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_order_refund' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET|PUT|DELETE /orders/<order_id>/refunds/<id>
|
||||
$routes[ $this->base . '/(?P<order_id>\d+)/refunds/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_order_refund' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_order_refund' ), WC_API_SERVER::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_order_refund' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'edit_order_refund' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_order_refund' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# POST|PUT /orders/bulk
|
||||
|
||||
@@ -37,7 +37,7 @@ class WC_API_Products extends WC_API_Resource {
|
||||
# GET/POST /products
|
||||
$routes[ $this->base ] = array(
|
||||
array( array( $this, 'get_products' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_product' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_product' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET /products/count
|
||||
@@ -75,7 +75,7 @@ class WC_API_Products extends WC_API_Resource {
|
||||
# GET/POST /products/attributes
|
||||
$routes[ $this->base . '/attributes' ] = array(
|
||||
array( array( $this, 'get_product_attributes' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_product_attribute' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_product_attribute' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET/PUT/DELETE /attributes/<id>
|
||||
|
||||
@@ -276,7 +276,7 @@ class WC_API_Server {
|
||||
|
||||
// Normalise the endpoints
|
||||
foreach ( $endpoints as $route => &$handlers ) {
|
||||
if ( count( $handlers ) <= 2 && isset( $handlers[1] ) && ! is_array( $handlers[1] ) ) {
|
||||
if ( is_array( $handlers ) && count( $handlers ) <= 2 && isset( $handlers[1] ) && ! is_array( $handlers[1] ) ) {
|
||||
$handlers = array( $handlers );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ class WC_API_Coupons extends WC_API_Resource {
|
||||
# GET/PUT/DELETE /coupons/<id>
|
||||
$routes[ $this->base . '/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_coupon' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_coupon' ), WC_API_SERVER::EDITABLE | WC_API_SERVER::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_coupon' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'edit_coupon' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_coupon' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# GET /coupons/code/<code>, note that coupon codes can contain spaces, dashes and underscores
|
||||
|
||||
@@ -58,35 +58,35 @@ class WC_API_Customers extends WC_API_Resource {
|
||||
|
||||
# GET/POST /customers
|
||||
$routes[ $this->base ] = array(
|
||||
array( array( $this, 'get_customers' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'create_customer' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'get_customers' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_customer' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET /customers/count
|
||||
$routes[ $this->base . '/count' ] = array(
|
||||
array( array( $this, 'get_customers_count' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customers_count' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET/PUT/DELETE /customers/<id>
|
||||
$routes[ $this->base . '/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_customer' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'edit_customer' ), WC_API_SERVER::EDITABLE | WC_API_SERVER::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_customer' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'get_customer' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_customer' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_customer' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/email/<email>
|
||||
$routes[ $this->base . '/email/(?P<email>.+)' ] = array(
|
||||
array( array( $this, 'get_customer_by_email' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customer_by_email' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/<id>/orders
|
||||
$routes[ $this->base . '/(?P<id>\d+)/orders' ] = array(
|
||||
array( array( $this, 'get_customer_orders' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customer_orders' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# GET /customers/<id>/downloads
|
||||
$routes[ $this->base . '/(?P<id>\d+)/downloads' ] = array(
|
||||
array( array( $this, 'get_customer_downloads' ), WC_API_SERVER::READABLE ),
|
||||
array( array( $this, 'get_customer_downloads' ), WC_API_Server::READABLE ),
|
||||
);
|
||||
|
||||
# POST|PUT /customers/bulk
|
||||
|
||||
@@ -62,27 +62,27 @@ class WC_API_Orders extends WC_API_Resource {
|
||||
# GET|POST /orders/<id>/notes
|
||||
$routes[ $this->base . '/(?P<order_id>\d+)/notes' ] = array(
|
||||
array( array( $this, 'get_order_notes' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_order_note' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_order_note' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET|PUT|DELETE /orders/<order_id>/notes/<id>
|
||||
$routes[ $this->base . '/(?P<order_id>\d+)/notes/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_order_note' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_order_note' ), WC_API_SERVER::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_order_note' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'edit_order_note' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_order_note' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# GET|POST /orders/<order_id>/refunds
|
||||
$routes[ $this->base . '/(?P<order_id>\d+)/refunds' ] = array(
|
||||
array( array( $this, 'get_order_refunds' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_order_refund' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_order_refund' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET|PUT|DELETE /orders/<order_id>/refunds/<id>
|
||||
$routes[ $this->base . '/(?P<order_id>\d+)/refunds/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_order_refund' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_order_refund' ), WC_API_SERVER::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_order_refund' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'edit_order_refund' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_order_refund' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# POST|PUT /orders/bulk
|
||||
|
||||
@@ -37,7 +37,7 @@ class WC_API_Products extends WC_API_Resource {
|
||||
# GET/POST /products
|
||||
$routes[ $this->base ] = array(
|
||||
array( array( $this, 'get_products' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_product' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_product' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET /products/count
|
||||
@@ -104,7 +104,7 @@ class WC_API_Products extends WC_API_Resource {
|
||||
# GET/POST /products/attributes
|
||||
$routes[ $this->base . '/attributes' ] = array(
|
||||
array( array( $this, 'get_product_attributes' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_product_attribute' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_product_attribute' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET/PUT/DELETE /products/attributes/<id>
|
||||
@@ -117,7 +117,7 @@ class WC_API_Products extends WC_API_Resource {
|
||||
# GET/POST /products/attributes/<attribute_id>/terms
|
||||
$routes[ $this->base . '/attributes/(?P<attribute_id>\d+)/terms' ] = array(
|
||||
array( array( $this, 'get_product_attribute_terms' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'create_product_attribute_term' ), WC_API_SERVER::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'create_product_attribute_term' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
);
|
||||
|
||||
# GET/PUT/DELETE /products/attributes/<attribute_id>/terms/<id>
|
||||
|
||||
@@ -276,7 +276,7 @@ class WC_API_Server {
|
||||
|
||||
// Normalise the endpoints
|
||||
foreach ( $endpoints as $route => &$handlers ) {
|
||||
if ( count( $handlers ) <= 2 && isset( $handlers[1] ) && ! is_array( $handlers[1] ) ) {
|
||||
if ( is_array( $handlers ) && count( $handlers ) <= 2 && isset( $handlers[1] ) && ! is_array( $handlers[1] ) ) {
|
||||
$handlers = array( $handlers );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ class WC_API_Taxes extends WC_API_Resource {
|
||||
# GET/PUT/DELETE /taxes/<id>
|
||||
$routes[ $this->base . '/(?P<id>\d+)' ] = array(
|
||||
array( array( $this, 'get_tax' ), WC_API_Server::READABLE ),
|
||||
array( array( $this, 'edit_tax' ), WC_API_SERVER::EDITABLE | WC_API_SERVER::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_tax' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'edit_tax' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
|
||||
array( array( $this, 'delete_tax' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# GET/POST /taxes/classes
|
||||
@@ -63,7 +63,7 @@ class WC_API_Taxes extends WC_API_Resource {
|
||||
|
||||
# GET /taxes/classes/<slug>
|
||||
$routes[ $this->base . '/classes/(?P<slug>\w[\w\s\-]*)' ] = array(
|
||||
array( array( $this, 'delete_tax_class' ), WC_API_SERVER::DELETABLE ),
|
||||
array( array( $this, 'delete_tax_class' ), WC_API_Server::DELETABLE ),
|
||||
);
|
||||
|
||||
# POST|PUT /taxes/bulk
|
||||
|
||||
@@ -121,6 +121,7 @@ final class Experimental_Abtest {
|
||||
*
|
||||
* @param string $test_name Name of the A/B test.
|
||||
* @return mixed|null A/B test variation, or null on failure.
|
||||
* @throws \Exception If there is an error retrieving the variation and the environment is not production.
|
||||
*/
|
||||
public function get_variation( $test_name ) {
|
||||
// Default to the control variation when users haven't consented to tracking.
|
||||
@@ -131,7 +132,11 @@ final class Experimental_Abtest {
|
||||
$variation = $this->fetch_variation( $test_name );
|
||||
|
||||
// If there was an error retrieving a variation, conceal the error for the consumer.
|
||||
// If there was an error retrieving a variation, throw an exception in non-production environments.
|
||||
if ( is_wp_error( $variation ) ) {
|
||||
if ( 'production' !== wp_get_environment_type() ) {
|
||||
throw new \Exception( $variation->get_error_message() );
|
||||
}
|
||||
return 'control';
|
||||
}
|
||||
|
||||
@@ -194,7 +199,7 @@ final class Experimental_Abtest {
|
||||
}
|
||||
|
||||
// Make sure test name is a valid one.
|
||||
if ( ! preg_match( '/^[A-Za-z0-9_]+$/', $test_name ) ) {
|
||||
if ( ! preg_match( '/^[a-z0-9_]+$/', $test_name ) ) {
|
||||
return new \WP_Error( 'invalid_test_name', 'Invalid A/B test name.' );
|
||||
}
|
||||
|
||||
|
||||
@@ -321,9 +321,9 @@ class WC_REST_Orders_V1_Controller extends WC_REST_Posts_Controller {
|
||||
foreach ( $order->get_items( 'coupon' ) as $coupon_item_id => $coupon_item ) {
|
||||
$coupon_line = array(
|
||||
'id' => $coupon_item_id,
|
||||
'code' => $coupon_item['name'],
|
||||
'discount' => wc_format_decimal( $coupon_item['discount_amount'], $dp ),
|
||||
'discount_tax' => wc_format_decimal( $coupon_item['discount_amount_tax'], $dp ),
|
||||
'code' => $coupon_item->get_name(),
|
||||
'discount' => wc_format_decimal( $coupon_item->get_discount(), $dp ),
|
||||
'discount_tax' => wc_format_decimal( $coupon_item->get_discount_tax(), $dp ),
|
||||
);
|
||||
|
||||
$data['coupon_lines'][] = $coupon_line;
|
||||
|
||||
@@ -289,6 +289,25 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
||||
);
|
||||
}
|
||||
|
||||
// Add additional applied coupon information.
|
||||
if ( $item instanceof WC_Order_Item_Coupon ) {
|
||||
$temp_coupon = new WC_Coupon();
|
||||
$coupon_info = $item->get_meta( 'coupon_info', true );
|
||||
if ( $coupon_info ) {
|
||||
$temp_coupon->set_short_info( $coupon_info );
|
||||
} else {
|
||||
$coupon_meta = $item->get_meta( 'coupon_data', true );
|
||||
if ( $coupon_meta ) {
|
||||
$temp_coupon->set_props( (array) $coupon_meta );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$data['discount_type'] = $temp_coupon->get_discount_type();
|
||||
$data['nominal_amount'] = (float) $temp_coupon->get_amount();
|
||||
$data['free_shipping'] = $temp_coupon->get_free_shipping();
|
||||
}
|
||||
|
||||
$data['meta_data'] = array_map(
|
||||
array( $this, 'merge_meta_item_with_formatted_meta_display_attributes' ),
|
||||
$data['meta_data'],
|
||||
@@ -1849,29 +1868,47 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'id' => array(
|
||||
'id' => array(
|
||||
'description' => __( 'Item ID.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'code' => array(
|
||||
'code' => array(
|
||||
'description' => __( 'Coupon code.', 'woocommerce' ),
|
||||
'type' => 'mixed',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'discount' => array(
|
||||
'discount' => array(
|
||||
'description' => __( 'Discount total.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'discount_tax' => array(
|
||||
'discount_tax' => array(
|
||||
'description' => __( 'Discount total tax.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'meta_data' => array(
|
||||
'discount_type' => array(
|
||||
'description' => __( 'Discount type.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'nominal_amount' => array(
|
||||
'description' => __( 'Discount amount as defined in the coupon (absolute value or a percent, depending on the discount type).', 'woocommerce' ),
|
||||
'type' => 'number',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'free_shipping' => array(
|
||||
'description' => __( 'Whether the coupon grants free shipping or not.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'meta_data' => array(
|
||||
'description' => __( 'Meta data.', 'woocommerce' ),
|
||||
'type' => 'array',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
|
||||
@@ -13,7 +13,8 @@ defined( 'ABSPATH' ) || exit;
|
||||
use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper;
|
||||
use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Download_Directories;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer as Order_DataSynchronizer;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
use Automattic\WooCommerce\Utilities\{ LoggingUtil, OrderUtil };
|
||||
|
||||
/**
|
||||
* System status controller class.
|
||||
*
|
||||
@@ -632,6 +633,44 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'logging' => array(
|
||||
'description' => __( 'Logging.', 'woocommerce' ),
|
||||
'type' => 'object',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'logging_enabled' => array(
|
||||
'description' => __( 'Is logging enabled?', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'default_handler' => array(
|
||||
'description' => __( 'The logging handler class.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'retention_period_days' => array(
|
||||
'description' => __( 'The number of days log entries are retained.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'level_threshold' => array(
|
||||
'description' => __( 'Minimum severity level.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'log_directory_size' => array(
|
||||
'description' => __( 'The size of the log directory.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -646,7 +685,7 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
||||
*/
|
||||
public function get_item_mappings() {
|
||||
return array(
|
||||
'environment' => $this->get_environment_info(),
|
||||
'environment' => $this->get_environment_info_per_fields( array( 'environment' ) ),
|
||||
'database' => $this->get_database_info(),
|
||||
'active_plugins' => $this->get_active_plugins(),
|
||||
'inactive_plugins' => $this->get_inactive_plugins(),
|
||||
@@ -656,6 +695,7 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
||||
'security' => $this->get_security_info(),
|
||||
'pages' => $this->get_pages(),
|
||||
'post_type_counts' => $this->get_post_type_counts(),
|
||||
'logging' => $this->get_logging_info(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -704,6 +744,9 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
||||
case 'post_type_counts':
|
||||
$items['post_type_counts'] = $this->get_post_type_counts();
|
||||
break;
|
||||
case 'logging':
|
||||
$items['logging'] = $this->get_logging_info();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1421,6 +1464,21 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
||||
return $pages_output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get info about the logging system.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_logging_info() {
|
||||
return array(
|
||||
'logging_enabled' => LoggingUtil::logging_is_enabled(),
|
||||
'default_handler' => LoggingUtil::get_default_handler(),
|
||||
'retention_period_days' => LoggingUtil::get_retention_period(),
|
||||
'level_threshold' => WC_Log_Levels::get_level_label( strtolower( LoggingUtil::get_level_threshold() ) ),
|
||||
'log_directory_size' => size_format( LoggingUtil::get_log_directory_size() ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any query params needed.
|
||||
*
|
||||
|
||||
@@ -65,6 +65,30 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the downloads for a product variation.
|
||||
*
|
||||
* @param WC_Product_Variation $product Product variation instance.
|
||||
* @param string $context Context of the request: 'view' or 'edit'.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_downloads( $product, $context = 'view' ) {
|
||||
$downloads = array();
|
||||
|
||||
if ( $product->is_downloadable() || 'edit' === $context ) {
|
||||
foreach ( $product->get_downloads() as $file_id => $file ) {
|
||||
$downloads[] = array(
|
||||
'id' => $file_id, // MD5 hash.
|
||||
'name' => $file['name'],
|
||||
'file' => $file['file'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $downloads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a single variation output for response.
|
||||
*
|
||||
@@ -95,7 +119,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
|
||||
'purchasable' => $object->is_purchasable(),
|
||||
'virtual' => $object->is_virtual(),
|
||||
'downloadable' => $object->is_downloadable(),
|
||||
'downloads' => $this->get_downloads( $object ),
|
||||
'downloads' => $this->get_downloads( $object, $context ),
|
||||
'download_limit' => '' !== $object->get_download_limit() ? (int) $object->get_download_limit() : -1,
|
||||
'download_expiry' => '' !== $object->get_download_expiry() ? (int) $object->get_download_expiry() : -1,
|
||||
'tax_status' => $object->get_tax_status(),
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Automattic\WooCommerce\RestApi;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
use Automattic\WooCommerce\RestApi\Utilities\SingletonTrait;
|
||||
|
||||
/**
|
||||
@@ -37,9 +38,14 @@ class Server {
|
||||
* Register REST API routes.
|
||||
*/
|
||||
public function register_rest_routes() {
|
||||
$container = wc_get_container();
|
||||
$legacy_proxy = $container->get( LegacyProxy::class );
|
||||
foreach ( $this->get_rest_namespaces() as $namespace => $controllers ) {
|
||||
foreach ( $controllers as $controller_name => $controller_class ) {
|
||||
$this->controllers[ $namespace ][ $controller_name ] = new $controller_class();
|
||||
$this->controllers[ $namespace ][ $controller_name ] =
|
||||
$container->has( $controller_class ) ?
|
||||
$container->get( $controller_class ) :
|
||||
$legacy_proxy->get_instance_of( $controller_class );
|
||||
$this->controllers[ $namespace ][ $controller_name ]->register_routes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
@@ -107,6 +108,7 @@ function wc_add_to_cart_message( $products, $show_qty = false, $return = false )
|
||||
$products = array_fill_keys( array_keys( $products ), 1 );
|
||||
}
|
||||
|
||||
$product_id = null;
|
||||
foreach ( $products as $product_id => $qty ) {
|
||||
/* translators: %s: product name */
|
||||
$titles[] = apply_filters( 'woocommerce_add_to_cart_qty_html', ( $qty > 1 ? absint( $qty ) . ' × ' : '' ), $product_id ) . apply_filters( 'woocommerce_add_to_cart_item_name_in_quotes', sprintf( _x( '“%s”', 'Item name in quotes', 'woocommerce' ), strip_tags( get_the_title( $product_id ) ) ), $product_id );
|
||||
@@ -393,6 +395,9 @@ function wc_get_chosen_shipping_method_ids() {
|
||||
$method_ids = array();
|
||||
$chosen_methods = WC()->session->get( 'chosen_shipping_methods', array() );
|
||||
foreach ( $chosen_methods as $chosen_method ) {
|
||||
if ( ! is_string( $chosen_method ) ) {
|
||||
continue;
|
||||
}
|
||||
$chosen_method = explode( ':', $chosen_method );
|
||||
$method_ids[] = current( $chosen_method );
|
||||
}
|
||||
@@ -405,7 +410,7 @@ function wc_get_chosen_shipping_method_ids() {
|
||||
* @since 3.2.0
|
||||
* @param int $key Key of package.
|
||||
* @param array $package Package data array.
|
||||
* @return string|bool
|
||||
* @return string|bool Either the chosen method ID or false if nothing is chosen yet.
|
||||
*/
|
||||
function wc_get_chosen_shipping_method_for_package( $key, $package ) {
|
||||
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
|
||||
@@ -421,6 +426,10 @@ function wc_get_chosen_shipping_method_for_package( $key, $package ) {
|
||||
$method_count = 0;
|
||||
}
|
||||
|
||||
if ( ! isset( $package['rates'] ) || ! is_array( $package['rates'] ) ) {
|
||||
$package['rates'] = array();
|
||||
}
|
||||
|
||||
// If not set, not available, or available methods have changed, set to the DEFAULT option.
|
||||
if ( ! $chosen_method || $changed || ! isset( $package['rates'][ $chosen_method ] ) || count( $package['rates'] ) !== $method_count ) {
|
||||
$chosen_method = wc_get_default_shipping_method_for_package( $key, $package, $chosen_method );
|
||||
@@ -441,25 +450,55 @@ function wc_get_chosen_shipping_method_for_package( $key, $package ) {
|
||||
* @since 3.2.0
|
||||
* @param int $key Key of package.
|
||||
* @param array $package Package data array.
|
||||
* @param string $chosen_method Chosen method id.
|
||||
* @param string $chosen_method Chosen shipping method. e.g. flat_rate:1.
|
||||
* @return string
|
||||
*/
|
||||
function wc_get_default_shipping_method_for_package( $key, $package, $chosen_method ) {
|
||||
$rate_keys = array_keys( $package['rates'] );
|
||||
$default = current( $rate_keys );
|
||||
$coupons = WC()->cart->get_coupons();
|
||||
foreach ( $coupons as $coupon ) {
|
||||
if ( $coupon->get_free_shipping() ) {
|
||||
foreach ( $rate_keys as $rate_key ) {
|
||||
if ( 0 === stripos( $rate_key, 'free_shipping' ) ) {
|
||||
$default = $rate_key;
|
||||
break;
|
||||
$chosen_method_id = current( explode( ':', $chosen_method ) );
|
||||
$rate_keys = array_keys( $package['rates'] );
|
||||
$chosen_method_exists = in_array( $chosen_method, $rate_keys, true );
|
||||
|
||||
/**
|
||||
* If the customer has selected local pickup, keep it selected if it's still in the package. We don't want to auto
|
||||
* toggle between shipping and pickup even if available shipping methods are changed.
|
||||
*
|
||||
* This is important for block based checkout where there is an explicit toggle between shipping and pickup.
|
||||
*/
|
||||
$local_pickup_method_ids = LocalPickupUtils::get_local_pickup_method_ids();
|
||||
$is_local_pickup_chosen = in_array( $chosen_method_id, $local_pickup_method_ids, true );
|
||||
|
||||
// Default to the first method in the package. This can be sorted in the backend by the merchant.
|
||||
$default = current( $rate_keys );
|
||||
|
||||
// Default to local pickup if its chosen already.
|
||||
if ( $chosen_method_exists && $is_local_pickup_chosen ) {
|
||||
$default = $chosen_method;
|
||||
|
||||
} else {
|
||||
// Check coupons to see if free shipping is available. If it is, we'll use that method as the default.
|
||||
$coupons = WC()->cart->get_coupons();
|
||||
foreach ( $coupons as $coupon ) {
|
||||
if ( $coupon->get_free_shipping() ) {
|
||||
foreach ( $rate_keys as $rate_key ) {
|
||||
if ( 0 === stripos( $rate_key, 'free_shipping' ) ) {
|
||||
$default = $rate_key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return apply_filters( 'woocommerce_shipping_chosen_method', $default, $package['rates'], $chosen_method );
|
||||
|
||||
/**
|
||||
* Filters the default shipping method for a package.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @param string $default Default shipping method.
|
||||
* @param array $rates Shipping rates.
|
||||
* @param string $chosen_method Chosen method id.
|
||||
*/
|
||||
return (string) apply_filters( 'woocommerce_shipping_chosen_method', $default, $package['rates'], $chosen_method );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -232,7 +232,7 @@ function wc_trim_zeros( $price ) {
|
||||
* @return float
|
||||
*/
|
||||
function wc_round_tax_total( $value, $precision = null ) {
|
||||
$precision = is_null( $precision ) ? wc_get_price_decimals() : intval( $precision );
|
||||
$precision = is_null( $precision ) ? wc_get_price_decimals() : intval( $precision );
|
||||
$rounded_tax = NumberUtil::round( $value, $precision, wc_get_tax_rounding_mode() ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.round_modeFound
|
||||
|
||||
return apply_filters( 'wc_round_tax_total', $rounded_tax, $value, $precision, WC_TAX_ROUNDING_MODE );
|
||||
@@ -371,15 +371,17 @@ function wc_format_coupon_code( $value ) {
|
||||
/**
|
||||
* Sanitize a coupon code.
|
||||
*
|
||||
* Uses sanitize_post_field since coupon codes are stored as
|
||||
* post_titles - the sanitization and escaping must match.
|
||||
* Uses sanitize_post_field since coupon codes are stored as post_titles - the sanitization and escaping must match.
|
||||
*
|
||||
* Due to the unfiltered_html captability that some (admin) users have, we need to account for slashes.
|
||||
*
|
||||
* @since 3.6.0
|
||||
* @param string $value Coupon code to format.
|
||||
* @return string
|
||||
*/
|
||||
function wc_sanitize_coupon_code( $value ) {
|
||||
return wp_filter_kses( sanitize_post_field( 'post_title', $value ?? '', 0, 'db' ) );
|
||||
$value = wp_kses( sanitize_post_field( 'post_title', $value ?? '', 0, 'db' ), 'entities' );
|
||||
return current_user_can( 'unfiltered_html' ) ? $value : stripslashes( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,14 +29,16 @@ function wc_notice_count( $notice_type = '' ) {
|
||||
$notice_count = 0;
|
||||
$all_notices = WC()->session->get( 'wc_notices', array() );
|
||||
|
||||
if ( isset( $all_notices[ $notice_type ] ) ) {
|
||||
if ( isset( $all_notices[ $notice_type ] ) && is_array( $all_notices[ $notice_type ] ) ) {
|
||||
|
||||
$notice_count = count( $all_notices[ $notice_type ] );
|
||||
|
||||
} elseif ( empty( $notice_type ) ) {
|
||||
|
||||
foreach ( $all_notices as $notices ) {
|
||||
$notice_count += count( $notices );
|
||||
if ( is_countable( $notices ) ) {
|
||||
$notice_count += count( $notices );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -561,7 +561,7 @@ function wc_create_refund( $args = array() ) {
|
||||
}
|
||||
|
||||
// Negative line items.
|
||||
if ( count( $args['line_items'] ) > 0 ) {
|
||||
if ( is_array( $args['line_items'] ) && count( $args['line_items'] ) > 0 ) {
|
||||
$items = $order->get_items( array( 'line_item', 'fee', 'shipping' ) );
|
||||
|
||||
foreach ( $items as $item_id => $item ) {
|
||||
|
||||
@@ -991,9 +991,7 @@ function wc_get_price_including_tax( $product, $args = array() ) {
|
||||
$price = '' !== $args['price'] ? max( 0.0, (float) $args['price'] ) : (float) $product->get_price();
|
||||
$qty = '' !== $args['qty'] ? max( 0.0, (float) $args['qty'] ) : 1;
|
||||
|
||||
if ( '' === $price ) {
|
||||
return '';
|
||||
} elseif ( empty( $qty ) ) {
|
||||
if ( empty( $qty ) ) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@@ -1080,9 +1078,7 @@ function wc_get_price_excluding_tax( $product, $args = array() ) {
|
||||
$price = '' !== $args['price'] ? max( 0.0, (float) $args['price'] ) : (float) $product->get_price();
|
||||
$qty = '' !== $args['qty'] ? max( 0.0, (float) $args['qty'] ) : 1;
|
||||
|
||||
if ( '' === $price ) {
|
||||
return '';
|
||||
} elseif ( empty( $qty ) ) {
|
||||
if ( empty( $qty ) ) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1435,8 +1435,8 @@ if ( ! function_exists( 'woocommerce_get_product_thumbnail' ) ) {
|
||||
* Get the product thumbnail, or the placeholder if not set.
|
||||
*
|
||||
* @param string $size (default: 'woocommerce_thumbnail').
|
||||
* @param array $attr Image attributes.
|
||||
* @param bool $placeholder True to return $placeholder if no image is found, or false to return an empty string.
|
||||
* @param array $attr Image attributes.
|
||||
* @param bool $placeholder True to return $placeholder if no image is found, or false to return an empty string.
|
||||
* @return string
|
||||
*/
|
||||
function woocommerce_get_product_thumbnail( $size = 'woocommerce_thumbnail', $attr = array(), $placeholder = true ) {
|
||||
@@ -2830,6 +2830,8 @@ if ( ! function_exists( 'woocommerce_form_field' ) ) {
|
||||
'default' => '',
|
||||
'autofocus' => '',
|
||||
'priority' => '',
|
||||
'unchecked_value' => null,
|
||||
'checked_value' => '1',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
@@ -2956,8 +2958,24 @@ if ( ! function_exists( 'woocommerce_form_field' ) ) {
|
||||
|
||||
break;
|
||||
case 'checkbox':
|
||||
$field = '<label class="checkbox ' . implode( ' ', $args['label_class'] ) . '" ' . implode( ' ', $custom_attributes ) . '>
|
||||
<input type="' . esc_attr( $args['type'] ) . '" class="input-checkbox ' . esc_attr( implode( ' ', $args['input_class'] ) ) . '" name="' . esc_attr( $key ) . '" id="' . esc_attr( $args['id'] ) . '" value="1" ' . checked( $value, 1, false ) . ' /> ' . $args['label'] . $required . '</label>';
|
||||
$field = '<label class="checkbox ' . esc_attr( implode( ' ', $args['label_class'] ) ) . '" ' . implode( ' ', $custom_attributes ) . '>';
|
||||
|
||||
// Output a hidden field so a value is POSTed if the box is not checked.
|
||||
if ( ! is_null( $args['unchecked_value'] ) ) {
|
||||
$field .= sprintf( '<input type="hidden" name="%1$s" value="%2$s" />', esc_attr( $key ), esc_attr( $args['unchecked_value'] ) );
|
||||
}
|
||||
|
||||
$field .= sprintf(
|
||||
'<input type="checkbox" name="%1$s" id="%2$s" value="%3$s" class="%4$s" %5$s /> %6$s',
|
||||
esc_attr( $key ),
|
||||
esc_attr( $args['id'] ),
|
||||
esc_attr( $args['checked_value'] ),
|
||||
esc_attr( 'input-checkbox ' . implode( ' ', $args['input_class'] ) ),
|
||||
checked( $value, $args['checked_value'], false ),
|
||||
wp_kses_post( $args['label'] )
|
||||
);
|
||||
|
||||
$field .= $required . '</label>';
|
||||
|
||||
break;
|
||||
case 'text':
|
||||
@@ -3487,7 +3505,7 @@ if ( ! function_exists( 'wc_display_item_downloads' ) ) {
|
||||
|
||||
$downloads = is_object( $item ) && $item->is_type( 'line_item' ) ? $item->get_item_downloads() : array();
|
||||
|
||||
if ( $downloads ) {
|
||||
if ( ! empty( $downloads ) ) {
|
||||
$i = 0;
|
||||
foreach ( $downloads as $file ) {
|
||||
$i ++;
|
||||
@@ -3836,30 +3854,32 @@ function wc_get_formatted_cart_item_data( $cart_item, $flat = false ) {
|
||||
// Filter item data to allow 3rd parties to add more to the array.
|
||||
$item_data = apply_filters( 'woocommerce_get_item_data', $item_data, $cart_item );
|
||||
|
||||
// Format item data ready to display.
|
||||
foreach ( $item_data as $key => $data ) {
|
||||
// Set hidden to true to not display meta on cart.
|
||||
if ( ! empty( $data['hidden'] ) ) {
|
||||
unset( $item_data[ $key ] );
|
||||
continue;
|
||||
}
|
||||
$item_data[ $key ]['key'] = ! empty( $data['key'] ) ? $data['key'] : $data['name'];
|
||||
$item_data[ $key ]['display'] = ! empty( $data['display'] ) ? $data['display'] : $data['value'];
|
||||
}
|
||||
|
||||
// Output flat or in list format.
|
||||
if ( count( $item_data ) > 0 ) {
|
||||
ob_start();
|
||||
|
||||
if ( $flat ) {
|
||||
foreach ( $item_data as $data ) {
|
||||
echo esc_html( $data['key'] ) . ': ' . wp_kses_post( $data['display'] ) . "\n";
|
||||
if ( is_array( $item_data ) ) {
|
||||
// Format item data ready to display.
|
||||
foreach ( $item_data as $key => $data ) {
|
||||
// Set hidden to true to not display meta on cart.
|
||||
if ( ! empty( $data['hidden'] ) ) {
|
||||
unset( $item_data[ $key ] );
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
wc_get_template( 'cart/cart-item-data.php', array( 'item_data' => $item_data ) );
|
||||
$item_data[ $key ]['key'] = ! empty( $data['key'] ) ? $data['key'] : $data['name'];
|
||||
$item_data[ $key ]['display'] = ! empty( $data['display'] ) ? $data['display'] : $data['value'];
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
// Output flat or in list format.
|
||||
if ( count( $item_data ) > 0 ) {
|
||||
ob_start();
|
||||
|
||||
if ( $flat ) {
|
||||
foreach ( $item_data as $data ) {
|
||||
echo esc_html( $data['key'] ) . ': ' . wp_kses_post( $data['display'] ) . "\n";
|
||||
}
|
||||
} else {
|
||||
wc_get_template( 'cart/cart-item-data.php', array( 'item_data' => $item_data ) );
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
|
||||
@@ -2649,3 +2649,21 @@ LIMIT 250
|
||||
function wc_update_860_remove_recommended_marketing_plugins_transient() {
|
||||
delete_transient( 'wc_marketing_recommended_plugins' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an .htaccess file and an empty index.html file to prevent listing of the default transient files directory,
|
||||
* if the directory exists.
|
||||
*/
|
||||
function wc_update_870_prevent_listing_of_transient_files_directory() {
|
||||
global $wp_filesystem;
|
||||
|
||||
$default_transient_files_dir = untrailingslashit( wp_upload_dir()['basedir'] ) . '/woocommerce_transient_files';
|
||||
if ( ! is_dir( $default_transient_files_dir ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/file.php';
|
||||
\WP_Filesystem();
|
||||
$wp_filesystem->put_contents( $default_transient_files_dir . '/.htaccess', 'deny from all' );
|
||||
$wp_filesystem->put_contents( $default_transient_files_dir . '/index.html', '' );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user