rebase on oct-10-2023
This commit is contained in:
@@ -195,6 +195,19 @@ abstract class WC_Data {
|
||||
* @return bool result
|
||||
*/
|
||||
public function delete( $force_delete = false ) {
|
||||
/**
|
||||
* Filters whether an object deletion should take place. Equivalent to `pre_delete_post`.
|
||||
*
|
||||
* @param mixed $check Whether to go ahead with deletion.
|
||||
* @param WC_Data $this The data object being deleted.
|
||||
* @param bool $force_delete Whether to bypass the trash.
|
||||
*
|
||||
* @since 8.1.0.
|
||||
*/
|
||||
$check = apply_filters( "woocommerce_pre_delete_$this->object_type", null, $this, $force_delete );
|
||||
if ( null !== $check ) {
|
||||
return $check;
|
||||
}
|
||||
if ( $this->data_store ) {
|
||||
$this->data_store->delete( $this, array( 'force_delete' => $force_delete ) );
|
||||
$this->set_id( 0 );
|
||||
@@ -761,7 +774,7 @@ abstract class WC_Data {
|
||||
if ( ! $errors ) {
|
||||
$errors = new WP_Error();
|
||||
}
|
||||
$errors->add( $e->getErrorCode(), $e->getMessage() );
|
||||
$errors->add( $e->getErrorCode(), $e->getMessage(), array( 'property_name' => $prop ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
/**
|
||||
* This method overwrites the base class's clone method to make it a no-op. In base class WC_Data, we are unsetting the meta_id to clone.
|
||||
* It seems like this was done to avoid conflicting the metadata when duplicating products. However, doing that does not seems necessary for orders.
|
||||
* In-fact, when we do that for orders, we lose the capability to clone orders with custom meta data by caching plugins. This is because, when we clone an order object for caching, it will clone the metadata without the ID. Unfortunately, when this cached object with nulled meta ID is retreived, WC_Data will consider it as a new meta and will insert it as a new meta-data causing duplicates.
|
||||
* In-fact, when we do that for orders, we lose the capability to clone orders with custom meta data by caching plugins. This is because, when we clone an order object for caching, it will clone the metadata without the ID. Unfortunately, when this cached object with nulled meta ID is retrieved, WC_Data will consider it as a new meta and will insert it as a new meta-data causing duplicates.
|
||||
*
|
||||
* Eventually, we should move away from overwriting the __clone method in base class itself, since it's easily possible to still duplicate the product without having to hook into the __clone method.
|
||||
*
|
||||
@@ -489,9 +489,9 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
*/
|
||||
public function get_total_discount( $ex_tax = true ) {
|
||||
if ( $ex_tax ) {
|
||||
$total_discount = $this->get_discount_total();
|
||||
$total_discount = (float) $this->get_discount_total();
|
||||
} else {
|
||||
$total_discount = $this->get_discount_total() + $this->get_discount_tax();
|
||||
$total_discount = (float) $this->get_discount_total() + (float) $this->get_discount_tax();
|
||||
}
|
||||
return apply_filters( 'woocommerce_order_get_total_discount', NumberUtil::round( $total_discount, WC_ROUNDING_PRECISION ), $this );
|
||||
}
|
||||
@@ -638,7 +638,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
}
|
||||
|
||||
// If the old status is set but unknown (e.g. draft) assume its pending for action usage.
|
||||
if ( $old_status && ! in_array( 'wc-' . $old_status, $this->get_valid_statuses(), true ) && ! in_array( $old_status, $status_exceptions, true ) ) {
|
||||
if ( $old_status && ( 'auto-draft' === $old_status || ( ! in_array( 'wc-' . $old_status, $this->get_valid_statuses(), true ) && ! in_array( $old_status, $status_exceptions, true ) ) ) ) {
|
||||
$old_status = 'pending';
|
||||
}
|
||||
}
|
||||
@@ -810,6 +810,16 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
* @param string $type Order item type. Default null.
|
||||
*/
|
||||
public function remove_order_items( $type = null ) {
|
||||
|
||||
/**
|
||||
* Trigger action before removing all order line items. Allows you to track order items.
|
||||
*
|
||||
* @param WC_Order $this The current order object.
|
||||
* @param string $type Order item type. Default null.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_remove_order_items', $this, $type );
|
||||
if ( ! empty( $type ) ) {
|
||||
$this->data_store->delete_items( $this, $type );
|
||||
|
||||
@@ -822,6 +832,15 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
$this->data_store->delete_items( $this );
|
||||
$this->items = array();
|
||||
}
|
||||
/**
|
||||
* Trigger action after removing all order line items.
|
||||
*
|
||||
* @param WC_Order $this The current order object.
|
||||
* @param string $type Order item type. Default null.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_removed_order_items', $this, $type );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1944,7 +1963,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
$subtotal = floatval( $item->get_subtotal() ) / $item->get_quantity();
|
||||
}
|
||||
|
||||
$subtotal = $round ? number_format( (float) $subtotal, wc_get_price_decimals(), '.', '' ) : $subtotal;
|
||||
$subtotal = $round ? NumberUtil::round( $subtotal, wc_get_price_decimals() ) : $subtotal;
|
||||
}
|
||||
|
||||
return apply_filters( 'woocommerce_order_amount_item_subtotal', $subtotal, $this, $item, $inc_tax, $round );
|
||||
@@ -2157,7 +2176,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
||||
} else {
|
||||
|
||||
// Show shipping including tax.
|
||||
$shipping = wc_price( $this->get_shipping_total() + $this->get_shipping_tax(), array( 'currency' => $this->get_currency() ) );
|
||||
$shipping = wc_price( (float) $this->get_shipping_total() + (float) $this->get_shipping_tax(), array( 'currency' => $this->get_currency() ) );
|
||||
|
||||
if ( (float) $this->get_shipping_tax() > 0 && ! $this->get_prices_include_tax() ) {
|
||||
$shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>', $this, $tax_display );
|
||||
|
||||
@@ -781,7 +781,9 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
||||
* @param string $visibility Options: 'hidden', 'visible', 'search' and 'catalog'.
|
||||
*/
|
||||
public function set_catalog_visibility( $visibility ) {
|
||||
$options = array_keys( wc_get_product_visibility_options() );
|
||||
$options = array_keys( wc_get_product_visibility_options() );
|
||||
$visibility = in_array( $visibility, $options, true ) ? $visibility : strtolower( $visibility );
|
||||
|
||||
if ( ! in_array( $visibility, $options, true ) ) {
|
||||
$this->error( 'product_invalid_catalog_visibility', __( 'Invalid catalog visibility option.', 'woocommerce' ) );
|
||||
}
|
||||
@@ -911,6 +913,8 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
||||
$status = 'taxable';
|
||||
}
|
||||
|
||||
$status = strtolower( $status );
|
||||
|
||||
if ( ! in_array( $status, $options, true ) ) {
|
||||
$this->error( 'product_invalid_tax_status', __( 'Invalid product tax status.', 'woocommerce' ) );
|
||||
}
|
||||
@@ -1110,7 +1114,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
||||
* position - integer sort order.
|
||||
* visible - If visible on frontend.
|
||||
* variation - If used for variations.
|
||||
* Indexed by unqiue key to allow clearing old ones after a set.
|
||||
* Indexed by unique key to allow clearing old ones after a set.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param array $raw_attributes Array of WC_Product_Attribute objects.
|
||||
@@ -1386,7 +1390,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
||||
return;
|
||||
}
|
||||
|
||||
$stock_is_above_notification_threshold = ( $this->get_stock_quantity() > get_option( 'woocommerce_notify_no_stock_amount', 0 ) );
|
||||
$stock_is_above_notification_threshold = ( (int) $this->get_stock_quantity() > absint( get_option( 'woocommerce_notify_no_stock_amount', 0 ) ) );
|
||||
$backorders_are_allowed = ( 'no' !== $this->get_backorders() );
|
||||
|
||||
if ( $stock_is_above_notification_threshold ) {
|
||||
@@ -1932,6 +1936,23 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
||||
return apply_filters( 'woocommerce_product_single_add_to_cart_text', __( 'Add to cart', 'woocommerce' ), $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the aria-describedby description for the add to cart button.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function add_to_cart_aria_describedby() {
|
||||
/**
|
||||
* Filter the aria-describedby description for the add to cart button.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*
|
||||
* @param string $var Text for the 'aria-describedby' attribute.
|
||||
* @param WC_Product $this Product object.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_product_add_to_cart_aria_describedby', '', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the add to cart button text.
|
||||
*
|
||||
|
||||
@@ -212,6 +212,21 @@ abstract class WC_Settings_API {
|
||||
if ( 'title' !== $this->get_field_type( $field ) ) {
|
||||
try {
|
||||
$this->settings[ $key ] = $this->get_field_value( $key, $field, $post_data );
|
||||
if ( 'select' === $field['type'] || 'checkbox' === $field['type'] ) {
|
||||
/**
|
||||
* Notify that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => $key,
|
||||
'type' => $field['type'],
|
||||
'value' => $this->settings[ $key ],
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$this->add_error( $e->getMessage() );
|
||||
}
|
||||
@@ -219,8 +234,8 @@ abstract class WC_Settings_API {
|
||||
}
|
||||
|
||||
$option_key = $this->get_option_key();
|
||||
do_action( 'woocommerce_update_option', array( 'id' => $option_key ) );
|
||||
return update_option( $option_key, apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $this->settings ), 'yes' );
|
||||
do_action( 'woocommerce_update_option', array( 'id' => $option_key ) ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
return update_option( $option_key, apply_filters( 'woocommerce_settings_api_sanitized_fields_' . $this->id, $this->settings ), 'yes' ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -729,7 +744,7 @@ abstract class WC_Settings_API {
|
||||
'options' => array(),
|
||||
);
|
||||
|
||||
$data = wp_parse_args( $data, $defaults );
|
||||
$data = wp_parse_args( $data, $defaults );
|
||||
$value = $this->get_option( $key );
|
||||
|
||||
ob_start();
|
||||
|
||||
@@ -134,6 +134,30 @@ abstract class WC_Shipping_Method extends WC_Settings_API {
|
||||
*/
|
||||
public $countries = array();
|
||||
|
||||
/**
|
||||
* Shipping method order.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $method_order;
|
||||
|
||||
/**
|
||||
* Whether the shipping method has settings or not. Preferably, use {@see has_settings()} instead.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $has_settings;
|
||||
|
||||
/**
|
||||
* When the method supports the settings modal, this is the admin settings HTML.
|
||||
* Preferably, use {@see get_admin_options_html()} instead.
|
||||
*
|
||||
* @var string|bool
|
||||
*/
|
||||
public $settings_html;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
@@ -340,7 +340,7 @@ class WC_Admin_Addons {
|
||||
$url
|
||||
);
|
||||
|
||||
echo '<a href="' . esc_url( $url ) . '" class="add-new-h2">' . esc_html( $text ) . '</a>' . "\n";
|
||||
echo '<a href="' . esc_url( $url ) . '" class="page-title-action">' . esc_html( $text ) . '</a>' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -641,7 +641,7 @@ class WC_Admin_Addons {
|
||||
|
||||
$defaults = array(
|
||||
'image' => WC()->plugin_url() . '/assets/images/wcpayments-icon-secure.png',
|
||||
'image_alt' => __( 'WooCommerce Payments', 'woocommerce' ),
|
||||
'image_alt' => __( 'WooPayments', 'woocommerce' ),
|
||||
'title' => __( 'Payments made simple, with no monthly fees — exclusively for WooCommerce stores.', 'woocommerce' ),
|
||||
'description' => __( 'Securely accept cards in your store. See payments, track cash flow into your bank account, and stay on top of disputes – right from your dashboard.', 'woocommerce' ),
|
||||
'button' => __( 'Free - Install now', 'woocommerce' ),
|
||||
@@ -1119,7 +1119,7 @@ class WC_Admin_Addons {
|
||||
/**
|
||||
* Install WooCommerce Payments from the Extensions screens.
|
||||
*
|
||||
* @param string $section Optional. Extenstions tab.
|
||||
* @param string $section Optional. Extensions tab.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -1128,7 +1128,7 @@ class WC_Admin_Addons {
|
||||
|
||||
$wcpay_plugin_id = 'woocommerce-payments';
|
||||
$wcpay_plugin = array(
|
||||
'name' => __( 'WooCommerce Payments', 'woocommerce' ),
|
||||
'name' => __( 'WooPayments', 'woocommerce' ),
|
||||
'repo-slug' => 'woocommerce-payments',
|
||||
);
|
||||
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ class WC_Admin_API_Keys_Table_List extends WP_List_Table {
|
||||
* @return string
|
||||
*/
|
||||
public function column_truncated_key( $key ) {
|
||||
return '<code>…' . esc_html( $key['truncated_key'] ) . '</code>';
|
||||
return '<code>***' . esc_html( $key['truncated_key'] ) . '</code>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -96,7 +96,7 @@ class WC_Admin_API_Keys {
|
||||
private static function table_list_output() {
|
||||
global $wpdb, $keys_table_list;
|
||||
|
||||
echo '<h2 class="wc-table-list-header">' . esc_html__( 'REST API', 'woocommerce' ) . ' <a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=keys&create-key=1' ) ) . '" class="add-new-h2">' . esc_html__( 'Add key', 'woocommerce' ) . '</a></h2>';
|
||||
echo '<h2 class="wc-table-list-header">' . esc_html__( 'REST API', 'woocommerce' ) . ' <a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=keys&create-key=1' ) ) . '" class="page-title-action">' . esc_html__( 'Add key', 'woocommerce' ) . '</a></h2>';
|
||||
|
||||
// Get the API keys count.
|
||||
$count = $wpdb->get_var( "SELECT COUNT(key_id) FROM {$wpdb->prefix}woocommerce_api_keys WHERE 1 = 1;" );
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
@@ -71,6 +72,8 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
if ( ! isset( $args['has_rtl'] ) ) {
|
||||
wp_style_add_data( $handle, 'rtl', 'replace' );
|
||||
}
|
||||
|
||||
wp_enqueue_style( $handle );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -197,6 +200,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
wp_enqueue_script( 'iris' );
|
||||
wp_enqueue_script( 'woocommerce_admin' );
|
||||
wp_enqueue_script( 'wc-enhanced-select' );
|
||||
|
||||
wp_enqueue_script( 'jquery-ui-sortable' );
|
||||
wp_enqueue_script( 'jquery-ui-autocomplete' );
|
||||
|
||||
@@ -222,10 +226,10 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
'export_products' => __( 'Export', 'woocommerce' ),
|
||||
),
|
||||
'nonces' => array(
|
||||
'gateway_toggle' => wp_create_nonce( 'woocommerce-toggle-payment-gateway-enabled' ),
|
||||
'gateway_toggle' => current_user_can( 'manage_woocommerce' ) ? wp_create_nonce( 'woocommerce-toggle-payment-gateway-enabled' ) : null,
|
||||
),
|
||||
'urls' => array(
|
||||
'add_product' => Features::is_enabled( 'new-product-management-experience' ) || Features::is_enabled( 'product-block-editor' ) ? esc_url_raw( admin_url( 'admin.php?page=wc-admin&path=/add-product' ) ) : null,
|
||||
'add_product' => Features::is_enabled( 'new-product-management-experience' ) || \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ? esc_url_raw( admin_url( 'admin.php?page=wc-admin&path=/add-product' ) ) : null,
|
||||
'import_products' => current_user_can( 'import' ) ? esc_url_raw( admin_url( 'edit.php?post_type=product&page=product_importer' ) ) : null,
|
||||
'export_products' => current_user_can( 'export' ) ? esc_url_raw( admin_url( 'edit.php?post_type=product&page=product_exporter' ) ) : null,
|
||||
),
|
||||
@@ -295,14 +299,13 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
'i18n_last_warning' => esc_js( __( 'Last warning, are you sure?', 'woocommerce' ) ),
|
||||
'i18n_choose_image' => esc_js( __( 'Choose an image', 'woocommerce' ) ),
|
||||
'i18n_set_image' => esc_js( __( 'Set variation image', 'woocommerce' ) ),
|
||||
'i18n_variation_added' => esc_js( __( 'variation added', 'woocommerce' ) ),
|
||||
'i18n_variations_added' => esc_js( __( 'variations added', 'woocommerce' ) ),
|
||||
'i18n_no_variations_added' => esc_js( __( 'No variations added', 'woocommerce' ) ),
|
||||
'i18n_variation_added' => esc_js( __( '1 variation added', 'woocommerce' ) ),
|
||||
'i18n_variations_added' => esc_js( __( '%qty% variations added', 'woocommerce' ) ),
|
||||
'i18n_remove_variation' => esc_js( __( 'Are you sure you want to remove this variation?', 'woocommerce' ) ),
|
||||
'i18n_scheduled_sale_start' => esc_js( __( 'Sale start date (YYYY-MM-DD format or leave blank)', 'woocommerce' ) ),
|
||||
'i18n_scheduled_sale_end' => esc_js( __( 'Sale end date (YYYY-MM-DD format or leave blank)', 'woocommerce' ) ),
|
||||
'i18n_edited_variations' => esc_js( __( 'Save changes before changing page?', 'woocommerce' ) ),
|
||||
'i18n_variation_count_single' => esc_js( __( '%qty% variation', 'woocommerce' ) ),
|
||||
'i18n_variation_count_single' => esc_js( __( '1 variation', 'woocommerce' ) ),
|
||||
'i18n_variation_count_plural' => esc_js( __( '%qty% variations', 'woocommerce' ) ),
|
||||
'variations_per_page' => absint( apply_filters( 'woocommerce_admin_meta_boxes_variations_per_page', 15 ) ),
|
||||
);
|
||||
@@ -373,7 +376,6 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
'i18n_delete_refund' => __( 'Are you sure you wish to delete this refund? This action cannot be undone.', 'woocommerce' ),
|
||||
'i18n_delete_tax' => __( 'Are you sure you wish to delete this tax column? This action cannot be undone.', 'woocommerce' ),
|
||||
'remove_item_meta' => __( 'Remove this item meta?', 'woocommerce' ),
|
||||
'remove_attribute' => __( 'Remove this attribute?', 'woocommerce' ),
|
||||
'name_label' => __( 'Name', 'woocommerce' ),
|
||||
'remove_label' => __( 'Remove', 'woocommerce' ),
|
||||
'click_to_toggle' => __( 'Click to toggle', 'woocommerce' ),
|
||||
@@ -415,7 +417,6 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
'rounding_precision' => wc_get_rounding_precision(),
|
||||
'tax_rounding_mode' => wc_get_tax_rounding_mode(),
|
||||
'product_types' => array_unique( array_merge( array( 'simple', 'grouped', 'variable', 'external' ), array_keys( wc_get_product_types() ) ) ),
|
||||
'has_local_attributes' => ! empty( wc_get_attribute_taxonomies() ),
|
||||
'i18n_download_permission_fail' => __( 'Could not grant access - the user may already have permission for this file or billing email is not set. Ensure the billing email is set, and the order has been saved.', 'woocommerce' ),
|
||||
'i18n_permission_revoke' => __( 'Are you sure you want to revoke access to this download?', 'woocommerce' ),
|
||||
'i18n_tax_rate_already_exists' => __( 'You cannot add the same tax rate twice!', 'woocommerce' ),
|
||||
@@ -434,6 +435,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
/* translators: %1$s: maximum file size */
|
||||
'i18n_product_image_tip' => sprintf( __( 'For best results, upload JPEG or PNG files that are 1000 by 1000 pixels or larger. Maximum upload file size: %1$s.', 'woocommerce' ) , size_format( wp_max_upload_size() ) ),
|
||||
'i18n_remove_used_attribute_confirmation_message' => __( 'If you remove this attribute, customers will no longer be able to purchase some variations of this product.', 'woocommerce' ),
|
||||
'i18n_add_attribute_error_notice' => __( 'Adding new attribute failed.', 'woocommerce' ),
|
||||
);
|
||||
|
||||
wp_localize_script( 'wc-admin-meta-boxes', 'woocommerce_admin_meta_boxes', $params );
|
||||
|
||||
@@ -91,7 +91,7 @@ if ( ! class_exists( 'WC_Admin_Dashboard_Setup', false ) ) :
|
||||
* @return string
|
||||
*/
|
||||
public function get_button_link( $task ) {
|
||||
$url = $task->get_json()['actionUrl'];
|
||||
$url = (string) $task->get_json()['actionUrl'];
|
||||
|
||||
if ( substr( $url, 0, 4 ) === 'http' ) {
|
||||
return $url;
|
||||
|
||||
@@ -290,13 +290,14 @@ class WC_Admin_Importers {
|
||||
// Send success.
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'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'] ),
|
||||
'failed' => count( $results['failed'] ),
|
||||
'updated' => count( $results['updated'] ),
|
||||
'skipped' => count( $results['skipped'] ),
|
||||
'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'] ),
|
||||
)
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -319,8 +319,7 @@ class WC_Admin_Menus {
|
||||
*/
|
||||
public function orders_menu(): void {
|
||||
if ( wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() ) {
|
||||
$this->orders_page_controller = new Custom_Orders_PageController();
|
||||
$this->orders_page_controller->setup();
|
||||
wc_get_container()->get( Custom_Orders_PageController::class )->setup();
|
||||
} else {
|
||||
wc_get_container()->get( COTRedirectionController::class )->setup();
|
||||
}
|
||||
@@ -427,7 +426,7 @@ class WC_Admin_Menus {
|
||||
* Maybe add new management product experience.
|
||||
*/
|
||||
public function maybe_add_new_product_management_experience() {
|
||||
if ( Features::is_enabled( 'new-product-management-experience' ) || Features::is_enabled( 'product-block-editor' ) ) {
|
||||
if ( Features::is_enabled( 'new-product-management-experience' ) || \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) {
|
||||
global $submenu;
|
||||
if ( isset( $submenu['edit.php?post_type=product'][10] ) ) {
|
||||
// Disable phpcs since we need to override submenu classes.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Internal\Admin\Orders\Edit as OrderEdit;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
@@ -215,7 +216,7 @@ class WC_Admin_Meta_Boxes {
|
||||
$post_id = absint( $post_id );
|
||||
|
||||
// $post_id and $post are required
|
||||
if ( empty( $post_id ) || empty( $post ) || self::$saved_meta_boxes ) {
|
||||
if ( empty( $post_id ) || empty( $post ) || ! is_a( $post, 'WP_Post' ) || self::$saved_meta_boxes ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -247,6 +248,10 @@ class WC_Admin_Meta_Boxes {
|
||||
|
||||
// Check the post type.
|
||||
if ( in_array( $post->post_type, wc_get_order_types( 'order-meta-boxes' ), true ) ) {
|
||||
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save meta for shop order.
|
||||
*
|
||||
|
||||
@@ -57,6 +57,7 @@ class WC_Admin_Notices {
|
||||
add_action( 'woocommerce_installed', array( __CLASS__, 'reset_admin_notices' ) );
|
||||
add_action( 'wp_loaded', array( __CLASS__, 'add_redirect_download_method_notice' ) );
|
||||
add_action( 'admin_init', array( __CLASS__, 'hide_notices' ), 20 );
|
||||
self::add_action( 'admin_init', array( __CLASS__, 'maybe_remove_php74_required_notice' ) );
|
||||
|
||||
// @TODO: This prevents Action Scheduler async jobs from storing empty list of notices during WC installation.
|
||||
// That could lead to OBW not starting and 'Run setup wizard' notice not appearing in WP admin, which we want
|
||||
@@ -119,8 +120,53 @@ class WC_Admin_Notices {
|
||||
self::add_notice( 'template_files' );
|
||||
self::add_min_version_notice();
|
||||
self::add_maxmind_missing_license_key_notice();
|
||||
self::maybe_add_php74_required_notice();
|
||||
}
|
||||
|
||||
// phpcs:disable Generic.Commenting.Todo.TaskFound
|
||||
|
||||
/**
|
||||
* Add an admin notice about the bump of the required PHP version in WooCommerce 8.2
|
||||
* if the current PHP version is too old.
|
||||
*
|
||||
* TODO: Remove this method in WooCommerce 8.2.
|
||||
*/
|
||||
private static function maybe_add_php74_required_notice() {
|
||||
if ( version_compare( phpversion(), '7.4', '>=' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::add_custom_notice(
|
||||
'php74_required_in_woo_82',
|
||||
sprintf(
|
||||
'%s%s',
|
||||
sprintf(
|
||||
'<h4>%s</h4>',
|
||||
esc_html__( 'PHP version requirements will change soon', 'woocommerce' )
|
||||
),
|
||||
sprintf(
|
||||
// translators: Placeholder is a URL.
|
||||
wpautop( wp_kses_data( __( 'WooCommerce 8.2, scheduled for <b>October 2023</b>, will require PHP 7.4 or newer to work. Your server is currently running an older version of PHP, so this change will impact your store. Upgrading to at least PHP 8.0 is recommended. <b><a href="%s">Learn more about this change.</a></b>', 'woocommerce' ) ) ),
|
||||
'https://developer.woocommerce.com/2023/06/05/new-requirement-for-woocommerce-8-2-php-7-4/'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the admin notice about the bump of the required PHP version in WooCommerce 8.2
|
||||
* if the current PHP version is good.
|
||||
*
|
||||
* TODO: Remove this method in WooCommerce 8.2.
|
||||
*/
|
||||
private static function maybe_remove_php74_required_notice() {
|
||||
if ( version_compare( phpversion(), '7.4', '>=' ) && self::has_notice( 'php74_required_in_woo_82' ) ) {
|
||||
self::remove_notice( 'php74_required_in_woo_82' );
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:enable Generic.Commenting.Todo.TaskFound
|
||||
|
||||
/**
|
||||
* Show a notice.
|
||||
*
|
||||
|
||||
@@ -985,7 +985,7 @@ class WC_Admin_Post_Types {
|
||||
break;
|
||||
}
|
||||
$regular_price = $product->get_regular_price();
|
||||
if ( $is_percentage ) {
|
||||
if ( $is_percentage && is_numeric( $regular_price ) ) {
|
||||
$percent = $price / 100;
|
||||
$new_price = max( 0, $regular_price - ( NumberUtil::round( $regular_price * $percent, wc_get_price_decimals() ) ) );
|
||||
} else {
|
||||
|
||||
@@ -429,8 +429,9 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||
|
||||
// Radio inputs.
|
||||
case 'radio':
|
||||
$option_value = $value['value'];
|
||||
$disabled_values = $value['disabled'] ?? array();
|
||||
$option_value = $value['value'];
|
||||
$disabled_values = $value['disabled'] ?? array();
|
||||
$show_desc_at_end = $value['desc_at_end'] ?? false;
|
||||
|
||||
?>
|
||||
<tr valign="top">
|
||||
@@ -439,7 +440,11 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||
</th>
|
||||
<td class="forminp forminp-<?php echo esc_attr( sanitize_title( $value['type'] ) ); ?>">
|
||||
<fieldset>
|
||||
<?php echo $description; // WPCS: XSS ok. ?>
|
||||
<?php
|
||||
if ( ! $show_desc_at_end ) {
|
||||
echo wp_kses_post( $description );
|
||||
}
|
||||
?>
|
||||
<ul>
|
||||
<?php
|
||||
foreach ( $value['options'] as $key => $val ) {
|
||||
@@ -458,6 +463,9 @@ if ( ! class_exists( 'WC_Admin_Settings', false ) ) :
|
||||
</li>
|
||||
<?php
|
||||
}
|
||||
if ( $show_desc_at_end ) {
|
||||
echo wp_kses_post( "<p class='description description-thin'>{$description}</p>" );
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</fieldset>
|
||||
|
||||
@@ -362,7 +362,7 @@ class WC_Admin_Status {
|
||||
<?php
|
||||
echo esc_html(
|
||||
sprintf(
|
||||
// translators: Comma seperated list of missing tables.
|
||||
// translators: Comma separated list of missing tables.
|
||||
__( 'Missing base tables: %s. Some WooCommerce functionality may not work as expected.', 'woocommerce' ),
|
||||
implode( ', ', $missing_tables )
|
||||
)
|
||||
|
||||
@@ -295,7 +295,7 @@ class WC_Admin_Webhooks {
|
||||
private static function table_list_output() {
|
||||
global $webhooks_table_list;
|
||||
|
||||
echo '<h2 class="wc-table-list-header">' . esc_html__( 'Webhooks', 'woocommerce' ) . ' <a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=webhooks&edit-webhook=0' ) ) . '" class="add-new-h2">' . esc_html__( 'Add webhook', 'woocommerce' ) . '</a></h2>';
|
||||
echo '<h2 class="wc-table-list-header">' . esc_html__( 'Webhooks', 'woocommerce' ) . ' <a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=advanced§ion=webhooks&edit-webhook=0' ) ) . '" class="page-title-action">' . esc_html__( 'Add webhook', 'woocommerce' ) . '</a></h2>';
|
||||
|
||||
// Get the webhooks count.
|
||||
$data_store = WC_Data_Store::load( 'webhook' );
|
||||
|
||||
@@ -28,7 +28,6 @@ class WC_Admin {
|
||||
add_action( 'admin_init', array( $this, 'admin_redirects' ) );
|
||||
add_action( 'admin_footer', 'wc_print_js', 25 );
|
||||
add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ), 1 );
|
||||
add_action( 'init', array( 'WC_Site_Tracking', 'init' ) );
|
||||
|
||||
// Disable WXR export of schedule action posts.
|
||||
add_filter( 'action_scheduler_post_type_args', array( $this, 'disable_webhook_post_export' ) );
|
||||
@@ -67,12 +66,6 @@ class WC_Admin {
|
||||
include_once __DIR__ . '/class-wc-admin-importers.php';
|
||||
include_once __DIR__ . '/class-wc-admin-exporters.php';
|
||||
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks.php';
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-event.php';
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-client.php';
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-footer-pixel.php';
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-site-tracking.php';
|
||||
|
||||
// Help Tabs.
|
||||
if ( apply_filters( 'woocommerce_enable_admin_help_tab', true ) ) {
|
||||
include_once __DIR__ . '/class-wc-admin-help.php';
|
||||
@@ -157,7 +150,19 @@ class WC_Admin {
|
||||
public function prevent_admin_access() {
|
||||
$prevent_access = false;
|
||||
|
||||
if ( apply_filters( 'woocommerce_disable_admin_bar', true ) && ! wp_doing_ajax() && isset( $_SERVER['SCRIPT_FILENAME'] ) && basename( sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_FILENAME'] ) ) ) !== 'admin-post.php' ) {
|
||||
// Do not interfere with admin-post or admin-ajax requests.
|
||||
$exempted_paths = array( 'admin-post.php', 'admin-ajax.php' );
|
||||
|
||||
if (
|
||||
/**
|
||||
* This filter is documented in ../wc-user-functions.php
|
||||
*
|
||||
* @since 3.6.0
|
||||
*/
|
||||
apply_filters( 'woocommerce_disable_admin_bar', true )
|
||||
&& isset( $_SERVER['SCRIPT_FILENAME'] )
|
||||
&& ! in_array( basename( sanitize_text_field( wp_unslash( $_SERVER['SCRIPT_FILENAME'] ) ) ), $exempted_paths, true )
|
||||
) {
|
||||
$has_cap = false;
|
||||
$access_caps = array( 'edit_posts', 'manage_woocommerce', 'view_admin_dashboard' );
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ class WC_Helper_Updater {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get translations updates informations.
|
||||
* Get translations updates information.
|
||||
*
|
||||
* Scans through all subscriptions for the connected user, as well
|
||||
* as all Woo extensions without a subscription, and obtains update
|
||||
@@ -226,7 +226,7 @@ class WC_Helper_Updater {
|
||||
$locales = apply_filters( 'plugins_update_check_locales', $locales );
|
||||
$locales = array_unique( $locales );
|
||||
|
||||
// No locales, the respone will be empty, we can return now.
|
||||
// No locales, the response will be empty, we can return now.
|
||||
if ( empty( $locales ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,8 @@
|
||||
$class_html = $current_filter === $key ? 'class="current"' : '';
|
||||
?>
|
||||
<li>
|
||||
<a <?php echo esc_html( $class_html ); ?> href="<?php echo esc_url( $url ); ?>">
|
||||
<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
<a <?php echo $class_html; ?> href="<?php echo esc_url( $url ); ?>">
|
||||
<?php echo esc_html( $label ); ?>
|
||||
<span class="count">(<?php echo absint( $counts[ $key ] ); ?>)</span>
|
||||
</a>
|
||||
|
||||
+7
-6
@@ -461,12 +461,13 @@ class WC_Product_CSV_Importer_Controller {
|
||||
*/
|
||||
protected function done() {
|
||||
check_admin_referer( 'woocommerce-csv-importer' );
|
||||
$imported = isset( $_GET['products-imported'] ) ? absint( $_GET['products-imported'] ) : 0;
|
||||
$updated = isset( $_GET['products-updated'] ) ? absint( $_GET['products-updated'] ) : 0;
|
||||
$failed = isset( $_GET['products-failed'] ) ? absint( $_GET['products-failed'] ) : 0;
|
||||
$skipped = isset( $_GET['products-skipped'] ) ? absint( $_GET['products-skipped'] ) : 0;
|
||||
$file_name = isset( $_GET['file-name'] ) ? sanitize_text_field( wp_unslash( $_GET['file-name'] ) ) : '';
|
||||
$errors = array_filter( (array) get_user_option( 'product_import_error_log' ) );
|
||||
$imported = isset( $_GET['products-imported'] ) ? absint( $_GET['products-imported'] ) : 0;
|
||||
$imported_variations = isset( $_GET['products-imported-variations'] ) ? absint( $_GET['products-imported-variations'] ) : 0;
|
||||
$updated = isset( $_GET['products-updated'] ) ? absint( $_GET['products-updated'] ) : 0;
|
||||
$failed = isset( $_GET['products-failed'] ) ? absint( $_GET['products-failed'] ) : 0;
|
||||
$skipped = isset( $_GET['products-skipped'] ) ? absint( $_GET['products-skipped'] ) : 0;
|
||||
$file_name = isset( $_GET['file-name'] ) ? sanitize_text_field( wp_unslash( $_GET['file-name'] ) ) : '';
|
||||
$errors = array_filter( (array) get_user_option( 'product_import_error_log' ) );
|
||||
|
||||
include_once dirname( __FILE__ ) . '/views/html-csv-import-done.php';
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ function wc_importer_current_locale() {
|
||||
* @return array
|
||||
*/
|
||||
function wc_importer_default_english_mappings( $mappings ) {
|
||||
if ( 'en_US' === wc_importer_current_locale() ) {
|
||||
if ( 'en_US' === wc_importer_current_locale() && is_array( $mappings ) && count( $mappings ) > 0 ) {
|
||||
return $mappings;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ add_filter( 'woocommerce_csv_product_import_mapping_default_columns', 'wc_import
|
||||
* @return array
|
||||
*/
|
||||
function wc_importer_default_special_english_mappings( $mappings ) {
|
||||
if ( 'en_US' === wc_importer_current_locale() ) {
|
||||
if ( 'en_US' === wc_importer_current_locale() && is_array( $mappings ) && count( $mappings ) > 0 ) {
|
||||
return $mappings;
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -30,6 +30,14 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
);
|
||||
}
|
||||
|
||||
if ( 0 < $imported_variations ) {
|
||||
$results[] = sprintf(
|
||||
/* translators: %d: products count */
|
||||
_n( '%s variations imported', '%s variations imported', $imported_variations, 'woocommerce' ),
|
||||
'<strong>' . number_format_i18n( $imported_variations ) . '</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
if ( 0 < $skipped ) {
|
||||
$results[] = sprintf(
|
||||
/* translators: %d: products count */
|
||||
|
||||
+20
-2
@@ -646,11 +646,11 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
|
||||
public function search_custom_fields( $wp ) {
|
||||
global $pagenow;
|
||||
|
||||
if ( 'edit.php' !== $pagenow || empty( $wp->query_vars['s'] ) || 'shop_order' !== $wp->query_vars['post_type'] || ! isset( $_GET['s'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
if ( 'edit.php' !== $pagenow || 'shop_order' !== $wp->query_vars['post_type'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
return;
|
||||
}
|
||||
|
||||
$post_ids = wc_order_search( wc_clean( wp_unslash( $_GET['s'] ) ) ); // WPCS: input var ok, sanitization ok.
|
||||
$post_ids = isset( $_GET['s'] ) && ! empty( $wp->query_vars['s'] ) ? wc_order_search( wc_clean( wp_unslash( $_GET['s'] ) ) ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
if ( ! empty( $post_ids ) ) {
|
||||
// Remove "s" - we don't want to search order name.
|
||||
@@ -662,5 +662,23 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
|
||||
// Search by found posts.
|
||||
$wp->query_vars['post__in'] = array_merge( $post_ids, array( 0 ) );
|
||||
}
|
||||
|
||||
if ( isset( $_GET['order_date_type'] ) && isset( $_GET['m'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$date_type = wc_clean( wp_unslash( $_GET['order_date_type'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
$date_query = wc_clean( wp_unslash( $_GET['m'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||||
// date_paid and date_completed are stored in postmeta, so we need to do a meta query.
|
||||
if ( 'date_paid' === $date_type || 'date_completed' === $date_type ) {
|
||||
$date_start = \DateTime::createFromFormat( 'Ymd H:i:s', "$date_query 00:00:00" );
|
||||
$date_end = \DateTime::createFromFormat( 'Ymd H:i:s', "$date_query 23:59:59" );
|
||||
|
||||
unset( $wp->query_vars['m'] );
|
||||
|
||||
if ( $date_start && $date_end ) {
|
||||
$wp->query_vars['meta_key'] = "_$date_type"; // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
|
||||
$wp->query_vars['meta_value'] = array( strval( $date_start->getTimestamp() ), strval( $date_end->getTimestamp() ) ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
|
||||
$wp->query_vars['meta_compare'] = 'BETWEEN';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -250,6 +250,21 @@ class WC_Meta_Box_Order_Data {
|
||||
|
||||
?>
|
||||
</p>
|
||||
<?php
|
||||
/**
|
||||
* Hook allowing extenders to render custom content
|
||||
* within the Order details box.
|
||||
*
|
||||
* This allows urgent notices or other important
|
||||
* order-related info to be displayed upfront in
|
||||
* the order page. Example: display a notice if
|
||||
* the order is disputed.
|
||||
*
|
||||
* @param $order WC_Order The order object being displayed.
|
||||
* @since 7.9.0
|
||||
*/
|
||||
do_action( 'woocommerce_admin_order_data_after_payment_info', $order );
|
||||
?>
|
||||
<div class="order_data_column_container">
|
||||
<div class="order_data_column">
|
||||
<h3><?php esc_html_e( 'General', 'woocommerce' ); ?></h3>
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* Product Categories meta box
|
||||
*
|
||||
* Display the product categories meta box.
|
||||
*
|
||||
* @package WooCommerce\Admin\Meta Boxes
|
||||
* @version 7.5.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* WC_Meta_Box_Product_Categories Class.
|
||||
*/
|
||||
class WC_Meta_Box_Product_Categories {
|
||||
|
||||
/**
|
||||
* Output the metabox.
|
||||
*
|
||||
* @param WP_Post $post Current post object.
|
||||
* @param array $box {
|
||||
* Categories meta box arguments.
|
||||
*
|
||||
* @type string $id Meta box 'id' attribute.
|
||||
* @type string $title Meta box title.
|
||||
* @type callable $callback Meta box display callback.
|
||||
* @type array $args {
|
||||
* Extra meta box arguments.
|
||||
*
|
||||
* @type string $taxonomy Taxonomy. Default 'category'.
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public static function output( $post, $box ) {
|
||||
$categories_count = (int) wp_count_terms( 'product_cat' );
|
||||
|
||||
/**
|
||||
* Filters the category metabox search threshold, for when to render the typeahead field.
|
||||
*
|
||||
* @since 7.6.0
|
||||
*
|
||||
* @param number $threshold The default threshold.
|
||||
* @returns number The threshold that will be used.
|
||||
*/
|
||||
if ( $categories_count <= apply_filters( 'woocommerce_product_category_metabox_search_threshold', 100 ) && function_exists( 'post_categories_meta_box' ) ) {
|
||||
return post_categories_meta_box( $post, $box );
|
||||
}
|
||||
|
||||
$defaults = array( 'taxonomy' => 'category' );
|
||||
if ( ! isset( $box['args'] ) || ! is_array( $box['args'] ) ) {
|
||||
$args = array();
|
||||
} else {
|
||||
$args = $box['args'];
|
||||
}
|
||||
$parsed_args = wp_parse_args( $args, $defaults );
|
||||
$tax_name = $parsed_args['taxonomy'];
|
||||
$selected_categories = wp_get_object_terms( $post->ID, 'product_cat' );
|
||||
?>
|
||||
<div id="taxonomy-<?php echo esc_attr( $tax_name ); ?>-metabox"></div>
|
||||
<?php foreach ( (array) $selected_categories as $term ) { ?>
|
||||
<input
|
||||
type="hidden"
|
||||
value="<?php echo esc_attr( $term->term_id ); ?>"
|
||||
name="tax_input[<?php esc_attr( $tax_name ); ?>][]"
|
||||
data-name="<?php echo esc_attr( $term->name ); ?>"
|
||||
/>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-24
@@ -56,22 +56,7 @@ class WC_Meta_Box_Product_Data {
|
||||
/* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */
|
||||
return apply_filters(
|
||||
'product_type_options',
|
||||
array(
|
||||
'virtual' => array(
|
||||
'id' => '_virtual',
|
||||
'wrapper_class' => 'show_if_simple',
|
||||
'label' => __( 'Virtual', 'woocommerce' ),
|
||||
'description' => __( 'Virtual products are intangible and are not shipped.', 'woocommerce' ),
|
||||
'default' => 'no',
|
||||
),
|
||||
'downloadable' => array(
|
||||
'id' => '_downloadable',
|
||||
'wrapper_class' => 'show_if_simple',
|
||||
'label' => __( 'Downloadable', 'woocommerce' ),
|
||||
'description' => __( 'Downloadable products give access to a file upon purchase.', 'woocommerce' ),
|
||||
'default' => 'no',
|
||||
),
|
||||
)
|
||||
wc_get_default_product_type_options(),
|
||||
);
|
||||
/* phpcs: enable */
|
||||
}
|
||||
@@ -186,14 +171,12 @@ class WC_Meta_Box_Product_Data {
|
||||
global $post, $wpdb, $product_object;
|
||||
|
||||
/* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */
|
||||
$global_attributes_count = count( wc_get_attribute_taxonomies() );
|
||||
$variation_attributes = array_filter( $product_object->get_attributes(), array( __CLASS__, 'filter_variation_attributes' ) );
|
||||
$non_variation_attributes_count = count( array_filter( $product_object->get_attributes(), array( __CLASS__, 'filter_non_variation_attributes' ) ) );
|
||||
$default_attributes = $product_object->get_default_attributes();
|
||||
$variations_count = absint( apply_filters( 'woocommerce_admin_meta_boxes_variations_count', $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'product_variation' AND post_status IN ('publish', 'private')", $post->ID ) ), $post->ID ) );
|
||||
$variations_per_page = absint( apply_filters( 'woocommerce_admin_meta_boxes_variations_per_page', 15 ) );
|
||||
$variations_total_pages = ceil( $variations_count / $variations_per_page );
|
||||
$modal_title = get_bloginfo( 'name' ) . __( ' says', 'woocommerce' );
|
||||
$variation_attributes = array_filter( $product_object->get_attributes(), array( __CLASS__, 'filter_variation_attributes' ) );
|
||||
$default_attributes = $product_object->get_default_attributes();
|
||||
$variations_count = absint( apply_filters( 'woocommerce_admin_meta_boxes_variations_count', $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'product_variation' AND post_status IN ('publish', 'private')", $post->ID ) ), $post->ID ) );
|
||||
$variations_per_page = absint( apply_filters( 'woocommerce_admin_meta_boxes_variations_per_page', 15 ) );
|
||||
$variations_total_pages = ceil( $variations_count / $variations_per_page );
|
||||
$modal_title = get_bloginfo( 'name' ) . __( ' says', 'woocommerce' );
|
||||
/* phpcs: enable */
|
||||
|
||||
include __DIR__ . '/views/html-product-data-variations.php';
|
||||
|
||||
+9
-18
@@ -93,28 +93,19 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
<tr>
|
||||
<td>
|
||||
<div class="enable_variation show_if_variable">
|
||||
<label><input type="checkbox" class="woocommerce_attribute_used_for_variations checkbox" <?php checked( $attribute->get_variation(), true ); ?> <?php echo esc_attr( isset( $is_variations_screen ) ? 'disabled' : '' ); ?> name="attribute_variation[<?php echo esc_attr( $i ); ?>]" value="1" /> <?php esc_html_e( 'Used for variations', 'woocommerce' ); ?></label>
|
||||
<?php
|
||||
if ( isset( $is_variations_screen ) ) {
|
||||
?>
|
||||
<input type="hidden" name="attribute_variation[<?php echo esc_attr( $i ); ?>]" value="1" />
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<label><input type="checkbox" class="woocommerce_attribute_used_for_variations checkbox" <?php checked( $attribute->get_variation(), true ); ?> name="attribute_variation[<?php echo esc_attr( $i ); ?>]" value="1" /> <?php esc_html_e( 'Used for variations', 'woocommerce' ); ?></label>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php
|
||||
if ( ! isset( $is_variations_screen ) ) {
|
||||
/**
|
||||
* Hook to display custom attribute terms.
|
||||
*
|
||||
* @since 3.4.0
|
||||
* @param WC_Product_Attribute $attribute Attribute object.
|
||||
* @param number $i Attribute index.
|
||||
*/
|
||||
do_action( 'woocommerce_after_product_attribute_settings', $attribute, $i );
|
||||
}
|
||||
/**
|
||||
* Hook to display custom attribute terms.
|
||||
*
|
||||
* @since 3.4.0
|
||||
* @param WC_Product_Attribute $attribute Attribute object.
|
||||
* @param number $i Attribute index.
|
||||
*/
|
||||
do_action( 'woocommerce_after_product_attribute_settings', $attribute, $i );
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
+17
-68
@@ -13,78 +13,27 @@ global $wc_product_attributes;
|
||||
// Array of defined attribute taxonomies.
|
||||
$attribute_taxonomies = wc_get_attribute_taxonomies();
|
||||
// Product attributes - taxonomies and custom, ordered, with visibility and variation attributes set.
|
||||
$product_attributes = $product_object->get_attributes( 'edit' );
|
||||
$has_local_attributes = empty( $attribute_taxonomies );
|
||||
$has_global_attributes = empty( $product_attributes );
|
||||
$is_add_global_attribute_visible = ! $has_local_attributes && $has_global_attributes;
|
||||
$icon_url = WC_ADMIN_IMAGES_FOLDER_URL . '/icons/global-attributes-icon.svg';
|
||||
$product_attributes = $product_object->get_attributes( 'edit' );
|
||||
?>
|
||||
<div id="product_attributes" class="panel wc-metaboxes-wrapper hidden">
|
||||
<div class="toolbar toolbar-top <?php echo $is_add_global_attribute_visible ? ' expand-close-hidden' : ''; ?>">
|
||||
<div class="add-global-attribute-container<?php echo $is_add_global_attribute_visible ? '' : ' hidden'; ?>">
|
||||
<div class="actions">
|
||||
<button type="button" class="button add_custom_attribute"><?php esc_html_e( 'Add new', 'woocommerce' ); ?></button>
|
||||
<select class="wc-attribute-search" data-placeholder="<?php esc_attr_e( 'Add existing', 'woocommerce' ); ?>" data-minimum-input-length="0">
|
||||
</select>
|
||||
</div>
|
||||
<div class="message">
|
||||
<img src="<?php echo esc_url( $icon_url ); ?>" />
|
||||
<p>
|
||||
<?php
|
||||
esc_html_e(
|
||||
'Add descriptive pieces of information that customers can use to search for this product on your store, such as “Material” or “Brand”.',
|
||||
'woocommerce'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="add-attribute-container<?php echo $is_add_global_attribute_visible ? ' hidden' : ' '; ?>">
|
||||
<?php
|
||||
if ( $has_local_attributes && $has_global_attributes ) :
|
||||
?>
|
||||
<div id="message" class="inline notice woocommerce-message">
|
||||
<p>
|
||||
<?php
|
||||
esc_html_e(
|
||||
'Add descriptive pieces of information that customers can use to search for this product on your store, such as “Material” or “Brand”.',
|
||||
'woocommerce'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<span class="expand-close">
|
||||
<a href="#" class="expand_all"><?php esc_html_e( 'Expand', 'woocommerce' ); ?></a> / <a href="#" class="close_all"><?php esc_html_e( 'Close', 'woocommerce' ); ?></a>
|
||||
</span>
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Filter for the attribute taxonomy filter dropdown threshold.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @param number $threshold The threshold for showing the simple dropdown.
|
||||
*/
|
||||
if ( count( $attribute_taxonomies ) <= apply_filters( 'woocommerce_attribute_taxonomy_filter_threshold', 20 ) ) :
|
||||
?>
|
||||
<select name="attribute_taxonomy" class="attribute_taxonomy">
|
||||
<option value=""><?php esc_html_e( 'Custom product attribute', 'woocommerce' ); ?></option>
|
||||
<div class="toolbar toolbar-top">
|
||||
<div id="message" class="inline notice woocommerce-message">
|
||||
<p>
|
||||
<?php
|
||||
if ( ! $has_local_attributes ) {
|
||||
foreach ( $attribute_taxonomies as $attr_taxonomy ) {
|
||||
$attribute_taxonomy_name = wc_attribute_taxonomy_name( $attr_taxonomy->attribute_name );
|
||||
$label = $attr_taxonomy->attribute_label ? $attr_taxonomy->attribute_label : $attr_taxonomy->attribute_name;
|
||||
echo '<option value="' . esc_attr( $attribute_taxonomy_name ) . '">' . esc_html( $label ) . '</option>';
|
||||
}
|
||||
}
|
||||
esc_html_e(
|
||||
'Add descriptive pieces of information that customers can use to search for this product on your store, such as “Material” or “Brand”.',
|
||||
'woocommerce'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<span class="expand-close">
|
||||
<a href="#" class="expand_all"><?php esc_html_e( 'Expand', 'woocommerce' ); ?></a> / <a href="#" class="close_all"><?php esc_html_e( 'Close', 'woocommerce' ); ?></a>
|
||||
</span>
|
||||
<div class="actions">
|
||||
<button type="button" class="button add_custom_attribute"><?php esc_html_e( 'Add new', 'woocommerce' ); ?></button>
|
||||
<select class="wc-attribute-search" data-placeholder="<?php esc_attr_e( 'Add existing', 'woocommerce' ); ?>" data-minimum-input-length="0">
|
||||
</select>
|
||||
<button type="button" class="button add_attribute"><?php esc_html_e( 'Add', 'woocommerce' ); ?></button>
|
||||
<?php else : ?>
|
||||
<button type="button" class="button add_custom_attribute"><?php esc_html_e( 'Add custom attribute', 'woocommerce' ); ?></button>
|
||||
<select class="wc-attribute-search attribute_taxonomy" id="attribute_taxonomy" name="attribute_taxonomy" data-placeholder="<?php esc_attr_e( 'Add existing attribute', 'woocommerce' ); ?>" data-minimum-input-length="0">
|
||||
</select>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="product_attributes wc-metaboxes">
|
||||
@@ -104,7 +53,7 @@ $icon_url = WC_ADMIN_IMAGES_FOLDER_URL . '/icons/global-a
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="toolbar toolbar-buttons<?php echo $is_add_global_attribute_visible ? ' hidden' : ''; ?>">
|
||||
<div class="toolbar toolbar-buttons">
|
||||
<span class="expand-close">
|
||||
<a href="#" class="expand_all"><?php esc_html_e( 'Expand', 'woocommerce' ); ?></a> / <a href="#" class="close_all"><?php esc_html_e( 'Close', 'woocommerce' ); ?></a>
|
||||
</span>
|
||||
|
||||
+3
-3
@@ -93,7 +93,7 @@ defined( 'ABSPATH' ) || exit;
|
||||
|
||||
if ( $downloadable_files ) {
|
||||
foreach ( $downloadable_files as $key => $file ) {
|
||||
$disabled_download = isset( $file['enabled'] ) && false === $file['enabled'];
|
||||
$disabled_download = isset( $file['enabled'] ) && false === $file['enabled'];
|
||||
$disabled_downloads_count += (int) $disabled_download;
|
||||
include __DIR__ . '/html-product-download.php';
|
||||
}
|
||||
@@ -105,8 +105,8 @@ defined( 'ABSPATH' ) || exit;
|
||||
<th colspan="2">
|
||||
<a href="#" class="button insert" data-row="
|
||||
<?php
|
||||
$key = '';
|
||||
$file = array(
|
||||
$key = '';
|
||||
$file = array(
|
||||
'file' => '',
|
||||
'name' => '',
|
||||
);
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
?>
|
||||
<label for="<?php echo esc_attr( $option['id'] ); ?>" class="<?php echo esc_attr( $option['wrapper_class'] ); ?> tips" data-tip="<?php echo esc_attr( $option['description'] ); ?>">
|
||||
<?php echo esc_html( $option['label'] ); ?>:
|
||||
<input type="checkbox" name="<?php echo esc_attr( $option['id'] ); ?>" id="<?php echo esc_attr( $option['id'] ); ?>" <?php echo checked( $selected_value, true, false ); ?> />
|
||||
<input type="checkbox" name="<?php echo esc_attr( $option['id'] ); ?>" id="<?php echo esc_attr( $option['id'] ); ?>" data-product-type-option-id="<?php echo esc_attr( $option['id'] ); ?>" <?php echo checked( $selected_value, true, false ); ?> />
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</span>
|
||||
|
||||
+1
-22
@@ -16,7 +16,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 ) && ( ( $global_attributes_count > 0 ) || ( $non_variation_attributes_count > 0 ) ) ) : ?>
|
||||
<?php if ( ! count( $variation_attributes ) ) : ?>
|
||||
|
||||
<div class="add-attributes-container">
|
||||
<div class="add-attributes-message">
|
||||
@@ -36,27 +36,6 @@ $arrow_img_url = WC_ADMIN_IMAGES_FOLDER_URL . '/product_data/no-variati
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php elseif ( ! count( $variation_attributes ) ) : ?>
|
||||
|
||||
<div id="message" class="inline notice woocommerce-message">
|
||||
<p>
|
||||
<?php echo esc_html_e( 'Offer customers multiple product options, like size and color. Start by creating a new custom attribute and enter available values (they’ll be shown as selectable product options).', 'woocommerce' ); ?> <a target="_blank" href="https://woocommerce.com/document/variable-product/#add-variations"><?php esc_html_e( 'Learn more about creating variations', 'woocommerce' ); ?></a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="wc-metabox">
|
||||
<div class="woocommerce_variation_new_attribute_data wc-metabox-content">
|
||||
<?php
|
||||
$i = 0;
|
||||
$is_variations_screen = true;
|
||||
$attribute = new WC_Product_Attribute();
|
||||
$attribute->set_variation( true );
|
||||
require __DIR__ . '/html-product-attribute-inner.php';
|
||||
?>
|
||||
<div class="toolbar">
|
||||
<button type="button" aria-disabled="true" class="button button-primary create-variations disabled"><?php esc_html_e( 'Create variations', 'woocommerce' ); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
|
||||
<div class="toolbar toolbar-variations-defaults">
|
||||
|
||||
@@ -512,7 +512,7 @@ class WC_Admin_Report {
|
||||
}
|
||||
|
||||
if ( $data_key ) {
|
||||
$prepared_data[ $time ][1] += $d->$data_key;
|
||||
$prepared_data[ $time ][1] += is_numeric( $d->$data_key ) ? $d->$data_key : 0;
|
||||
} else {
|
||||
$prepared_data[ $time ][1] ++;
|
||||
}
|
||||
|
||||
+1
-1
@@ -326,7 +326,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
|
||||
);
|
||||
|
||||
foreach ( $this->report_data->partial_refunds as $key => $order ) {
|
||||
$this->report_data->partial_refunds[ $key ]->net_refund = $order->total_refund - ( $order->total_shipping + $order->total_tax + $order->total_shipping_tax );
|
||||
$this->report_data->partial_refunds[ $key ]->net_refund = (float) $order->total_refund - ( (float) $order->total_shipping + (float) $order->total_tax + (float) $order->total_shipping_tax );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-3
@@ -158,22 +158,28 @@ class WC_Report_Taxes_By_Code extends WC_Admin_Report {
|
||||
|
||||
// Merge.
|
||||
$tax_rows = array();
|
||||
// Initialize an associative array to store unique post_ids.
|
||||
$unique_post_ids = array();
|
||||
|
||||
foreach ( $tax_rows_orders + $tax_rows_partial_refunds as $tax_row ) {
|
||||
$key = $tax_row->rate_id;
|
||||
$key = $tax_row->tax_rate;
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
'tax_amount' => 0,
|
||||
'shipping_tax_amount' => 0,
|
||||
'total_orders' => 0,
|
||||
);
|
||||
$tax_rows[ $key ]->total_orders += 1;
|
||||
$tax_rows[ $key ]->tax_rate = $tax_row->tax_rate;
|
||||
$tax_rows[ $key ]->tax_amount += wc_round_tax_total( $tax_row->tax_amount );
|
||||
$tax_rows[ $key ]->shipping_tax_amount += wc_round_tax_total( $tax_row->shipping_tax_amount );
|
||||
if ( ! isset( $unique_post_ids[ $key ] ) || ! in_array( $tax_row->post_id, $unique_post_ids[ $key ], true ) ) {
|
||||
$unique_post_ids[ $key ] = isset( $unique_post_ids[ $key ] ) ? $unique_post_ids[ $key ] : array();
|
||||
$unique_post_ids[ $key ][] = $tax_row->post_id;
|
||||
$tax_rows[ $key ]->total_orders += 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $tax_rows_full_refunds as $tax_row ) {
|
||||
$key = $tax_row->rate_id;
|
||||
$key = $tax_row->tax_rate;
|
||||
$tax_rows[ $key ] = isset( $tax_rows[ $key ] ) ? $tax_rows[ $key ] : (object) array(
|
||||
'tax_amount' => 0,
|
||||
'shipping_tax_amount' => 0,
|
||||
|
||||
+2
-1
@@ -193,7 +193,8 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page {
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Manage the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . strtolower( $gateway->id ) ) ) . '">' . esc_html__( 'Manage', 'woocommerce' ) . '</a>';
|
||||
} else {
|
||||
if (
|
||||
'WooCommerce Payments' === $method_title &&
|
||||
// Keep old brand name for backwards compatibility.
|
||||
( 'WooCommerce Payments' === $method_title || 'WooPayments' === $method_title ) &&
|
||||
class_exists( 'WC_Payments_Account' )
|
||||
) {
|
||||
$setup_url = WC_Payments_Account::get_connect_url();
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ class WC_Settings_Products extends WC_Settings_Page {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings for the detault section.
|
||||
* Get settings for the default section.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
||||
+9
-7
@@ -287,13 +287,15 @@ class WC_Settings_Shipping extends WC_Settings_Page {
|
||||
'zone_id' => $zone->get_id(),
|
||||
'wc_shipping_zones_nonce' => wp_create_nonce( 'wc_shipping_zones_nonce' ),
|
||||
'strings' => array(
|
||||
'unload_confirmation_msg' => __( 'Your changed data will be lost if you leave this page without saving.', 'woocommerce' ),
|
||||
'save_changes_prompt' => __( 'Do you wish to save your changes first? Your changed data will be discarded if you choose to cancel.', 'woocommerce' ),
|
||||
'save_failed' => __( 'Your changes were not saved. Please retry.', 'woocommerce' ),
|
||||
'add_method_failed' => __( 'Shipping method could not be added. Please retry.', 'woocommerce' ),
|
||||
'yes' => __( 'Yes', 'woocommerce' ),
|
||||
'no' => __( 'No', 'woocommerce' ),
|
||||
'default_zone_name' => __( 'Zone', 'woocommerce' ),
|
||||
'unload_confirmation_msg' => __( 'Your changed data will be lost if you leave this page without saving.', 'woocommerce' ),
|
||||
'save_changes_prompt' => __( 'Do you wish to save your changes first? Your changed data will be discarded if you choose to cancel.', 'woocommerce' ),
|
||||
'save_failed' => __( 'Your changes were not saved. Please retry.', 'woocommerce' ),
|
||||
'add_method_failed' => __( 'Shipping method could not be added. Please retry.', 'woocommerce' ),
|
||||
'remove_method_failed' => __( 'Shipping method could not be removed. Please retry.', 'woocommerce' ),
|
||||
'yes' => __( 'Yes', 'woocommerce' ),
|
||||
'no' => __( 'No', 'woocommerce' ),
|
||||
'default_zone_name' => __( 'Zone', 'woocommerce' ),
|
||||
'delete_shipping_method_confirmation' => __( 'Are you sure you want to delete this shipping method?', 'woocommerce' ),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -57,6 +57,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="rates-bottom-pagination"></div>
|
||||
|
||||
<script type="text/html" id="tmpl-wc-tax-table-row">
|
||||
<tr class="tips" data-tip="<?php printf( esc_attr__( 'Tax rate ID: %s', 'woocommerce' ), '{{ data.tax_rate_id }}' ); ?>" data-id="{{ data.tax_rate_id }}">
|
||||
<td class="country">
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
const currentStep = widget.data( 'current-step' );
|
||||
const totalSteps = widget.data( 'total-steps' );
|
||||
|
||||
$( document ).on( 'ready', function() {
|
||||
$( function() {
|
||||
window.wcTracks.recordEvent( 'wcadmin_setup_widget_view', {
|
||||
completed_tasks: currentStep,
|
||||
total_tasks: totalSteps,
|
||||
|
||||
@@ -136,10 +136,9 @@ $current_section_name = __( 'Browse Categories', 'woocommerce' );
|
||||
|
||||
<?php if ( 'Storefront' !== $theme['Name'] && '_featured' !== $current_section ) : ?>
|
||||
<?php
|
||||
$storefront_url = WC_Admin_Addons::add_in_app_purchase_url_params( 'https://woocommerce.com/storefront/?utm_source=extensionsscreen&utm_medium=product&utm_campaign=wcaddon' );
|
||||
$storefront_url = WC_Admin_Addons::add_in_app_purchase_url_params( 'https://woocommerce.com/products/storefront/?utm_source=extensionsscreen&utm_medium=product&utm_campaign=wcaddon' );
|
||||
?>
|
||||
<div class="storefront">
|
||||
<a href="<?php echo esc_url( $storefront_url ); ?>" target="_blank"><img src="<?php echo esc_url( WC()->plugin_url() ); ?>/assets/images/storefront.png" alt="<?php esc_attr_e( 'Storefront', 'woocommerce' ); ?>" /></a>
|
||||
<h2><?php esc_html_e( 'Looking for a WooCommerce theme?', 'woocommerce' ); ?></h2>
|
||||
<p><?php echo wp_kses_post( __( 'We recommend Storefront, the <em>official</em> WooCommerce theme.', 'woocommerce' ) ); ?></p>
|
||||
<p><?php echo wp_kses_post( __( 'Storefront is an intuitive, flexible and <strong>free</strong> WordPress theme offering deep integration with WooCommerce and many of the most popular customer-facing extensions.', 'woocommerce' ) ); ?></p>
|
||||
|
||||
+2
-2
@@ -729,7 +729,7 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
||||
<td><?php echo esc_html( $settings['number_of_decimals'] ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-export-label="Taxonomies: Product Types"><?php esc_html_e( 'Taxonomies: Product types', 'woocommerce' ); ?></th>
|
||||
<td data-export-label="Taxonomies: Product Types"><?php esc_html_e( 'Taxonomies: Product types', 'woocommerce' ); ?></td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'A list of taxonomy terms that can be used in regard to order/product statuses.', 'woocommerce' ) ); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?></td>
|
||||
<td>
|
||||
<?php
|
||||
@@ -742,7 +742,7 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) :
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-export-label="Taxonomies: Product Visibility"><?php esc_html_e( 'Taxonomies: Product visibility', 'woocommerce' ); ?></th>
|
||||
<td data-export-label="Taxonomies: Product Visibility"><?php esc_html_e( 'Taxonomies: Product visibility', 'woocommerce' ); ?></td>
|
||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'A list of taxonomy terms used for product visibility.', 'woocommerce' ) ); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?></td>
|
||||
<td>
|
||||
<?php
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ defined( 'ABSPATH' ) || exit;
|
||||
if ( $verify_db_tool_available ) {
|
||||
echo wp_kses_post(
|
||||
sprintf(
|
||||
/* translators: %1%s: Missing tables (seperated by ",") %2$s: Link to check again */
|
||||
/* translators: %1%s: Missing tables (separated by ",") %2$s: Link to check again */
|
||||
__( 'One or more tables required for WooCommerce to function are missing, some features may not work as expected. Missing tables: %1$s. <a href="%2$s">Check again.</a>', 'woocommerce' ),
|
||||
esc_html( implode( ', ', $missing_tables ) ),
|
||||
wp_nonce_url( admin_url( 'admin.php?page=wc-status&tab=tools&action=verify_db_tables' ), 'debug_action' )
|
||||
@@ -32,7 +32,7 @@ defined( 'ABSPATH' ) || exit;
|
||||
} else {
|
||||
echo wp_kses_post(
|
||||
sprintf(
|
||||
/* translators: %1%s: Missing tables (seperated by ",") */
|
||||
/* translators: %1%s: Missing tables (separated by ",") */
|
||||
__( 'One or more tables required for WooCommerce to function are missing, some features may not work as expected. Missing tables: %1$s.', 'woocommerce' ),
|
||||
esc_html( implode( ', ', $missing_tables ) )
|
||||
)
|
||||
|
||||
@@ -508,18 +508,20 @@ function wc_render_invalid_variation_notice( $product_object ) {
|
||||
|
||||
// Check if a variation exists without pricing data.
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$invalid_variation_count = $wpdb->get_var(
|
||||
$valid_variation_count = $wpdb->get_var(
|
||||
"
|
||||
SELECT count(post_id) FROM {$wpdb->postmeta}
|
||||
WHERE post_id in (" . implode( ',', array_map( 'absint', $variation_ids ) ) . ")
|
||||
AND ( meta_key='_subscription_sign_up_fee' OR meta_key='_price' )
|
||||
AND meta_value > 0
|
||||
AND meta_value >= 0
|
||||
AND meta_value != ''
|
||||
"
|
||||
);
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
if ( 0 < ( $variation_count - $invalid_variation_count ) ) {
|
||||
$invalid_variation_count = $variation_count - $valid_variation_count;
|
||||
|
||||
if ( 0 < $invalid_variation_count ) {
|
||||
?>
|
||||
<div id="message" class="inline notice notice-warning woocommerce-message woocommerce-notice-invalid-variation">
|
||||
<p>
|
||||
@@ -527,8 +529,8 @@ function wc_render_invalid_variation_notice( $product_object ) {
|
||||
echo wp_kses_post(
|
||||
sprintf(
|
||||
/* Translators: %d variation count. */
|
||||
_n( '%d variation does not have a price.', '%d variations do not have prices.', ( $variation_count - $invalid_variation_count ), 'woocommerce' ),
|
||||
( $variation_count - $invalid_variation_count )
|
||||
_n( '%d variation does not have a price.', '%d variations do not have prices.', $invalid_variation_count, 'woocommerce' ),
|
||||
$invalid_variation_count
|
||||
) . ' ' .
|
||||
__( 'Variations (and their attributes) that do not have prices will not be shown in your store.', 'woocommerce' )
|
||||
);
|
||||
@@ -561,3 +563,29 @@ function wc_get_current_admin_url() {
|
||||
|
||||
return remove_query_arg( array( '_wpnonce', '_wc_notice_nonce', 'wc_db_update', 'wc_db_update_nonce', 'wc-hide-notice' ), admin_url( $uri ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default product type options.
|
||||
*
|
||||
* @internal
|
||||
* @since 7.9.0
|
||||
* @return array
|
||||
*/
|
||||
function wc_get_default_product_type_options() {
|
||||
return array(
|
||||
'virtual' => array(
|
||||
'id' => '_virtual',
|
||||
'wrapper_class' => 'show_if_simple',
|
||||
'label' => __( 'Virtual', 'woocommerce' ),
|
||||
'description' => __( 'Virtual products are intangible and are not shipped.', 'woocommerce' ),
|
||||
'default' => 'no',
|
||||
),
|
||||
'downloadable' => array(
|
||||
'id' => '_downloadable',
|
||||
'wrapper_class' => 'show_if_simple',
|
||||
'label' => __( 'Downloadable', 'woocommerce' ),
|
||||
'description' => __( 'Downloadable products give access to a file upon purchase.', 'woocommerce' ),
|
||||
'default' => 'no',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -157,6 +157,7 @@ class WC_AJAX {
|
||||
'json_search_downloadable_products_and_variations',
|
||||
'json_search_customers',
|
||||
'json_search_categories',
|
||||
'json_search_categories_tree',
|
||||
'json_search_taxonomy_terms',
|
||||
'json_search_product_attributes',
|
||||
'json_search_pages',
|
||||
@@ -172,6 +173,7 @@ class WC_AJAX {
|
||||
'tax_rates_save_changes',
|
||||
'shipping_zones_save_changes',
|
||||
'shipping_zone_add_method',
|
||||
'shipping_zone_remove_method',
|
||||
'shipping_zone_methods_save_changes',
|
||||
'shipping_zone_methods_save_settings',
|
||||
'shipping_classes_save_changes',
|
||||
@@ -195,6 +197,22 @@ class WC_AJAX {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// WP's heartbeat.
|
||||
$ajax_heartbeat_callbacks = array(
|
||||
'order_refresh_lock',
|
||||
'check_locked_orders',
|
||||
);
|
||||
foreach ( $ajax_heartbeat_callbacks as $ajax_callback ) {
|
||||
add_filter(
|
||||
'heartbeat_received',
|
||||
function( $response, $data ) use ( $ajax_callback ) {
|
||||
return call_user_func_array( array( __CLASS__, $ajax_callback ), func_get_args() );
|
||||
},
|
||||
10,
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +316,12 @@ class WC_AJAX {
|
||||
'fragments' => apply_filters(
|
||||
'woocommerce_update_order_review_fragments',
|
||||
array(
|
||||
'form.woocommerce-checkout' => '<div class="woocommerce-error">' . __( 'Sorry, your session has expired.', 'woocommerce' ) . ' <a href="' . esc_url( wc_get_page_permalink( 'shop' ) ) . '" class="wc-backward">' . __( 'Return to shop', 'woocommerce' ) . '</a></div>',
|
||||
'form.woocommerce-checkout' => wc_print_notice(
|
||||
esc_html__( 'Sorry, your session has expired.', 'woocommerce' ) . ' <a href="' . esc_url( wc_get_page_permalink( 'shop' ) ) . '" class="wc-backward">' . esc_html__( 'Return to shop', 'woocommerce' ) . '</a>',
|
||||
'error',
|
||||
array(),
|
||||
true
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
@@ -588,6 +611,8 @@ class WC_AJAX {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
$product_type = isset( $_POST['product_type'] ) ? sanitize_text_field( wp_unslash( $_POST['product_type'] ) ) : 'simple';
|
||||
|
||||
$i = absint( $_POST['i'] );
|
||||
$metabox_class = array();
|
||||
$attribute = new WC_Product_Attribute();
|
||||
@@ -596,7 +621,13 @@ class WC_AJAX {
|
||||
$attribute->set_name( sanitize_text_field( wp_unslash( $_POST['taxonomy'] ) ) );
|
||||
/* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */
|
||||
$attribute->set_visible( apply_filters( 'woocommerce_attribute_default_visibility', 1 ) );
|
||||
$attribute->set_variation( apply_filters( 'woocommerce_attribute_default_is_variation', 1 ) );
|
||||
$attribute->set_variation(
|
||||
apply_filters(
|
||||
'woocommerce_attribute_default_is_variation',
|
||||
'variable' === $product_type ? 1 : 0,
|
||||
$product_type
|
||||
)
|
||||
);
|
||||
/* phpcs: enable */
|
||||
|
||||
if ( $attribute->is_taxonomy() ) {
|
||||
@@ -940,6 +971,8 @@ class WC_AJAX {
|
||||
$data['date_created'] = $data['date_created'] ? $data['date_created']->getTimestamp() : null;
|
||||
$data['date_modified'] = $data['date_modified'] ? $data['date_modified']->getTimestamp() : null;
|
||||
|
||||
unset( $data['meta_data'] );
|
||||
|
||||
$customer_data = apply_filters( 'woocommerce_ajax_get_customer_details', $data, $customer, $user_id );
|
||||
wp_send_json( $customer_data );
|
||||
}
|
||||
@@ -1764,12 +1797,13 @@ class WC_AJAX {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$show_empty = isset( $_GET['show_empty'] ) ? wp_validate_boolean( wc_clean( wp_unslash( $_GET['show_empty'] ) ) ) : false;
|
||||
$found_categories = array();
|
||||
$args = array(
|
||||
'taxonomy' => array( 'product_cat' ),
|
||||
'orderby' => 'id',
|
||||
'order' => 'ASC',
|
||||
'hide_empty' => true,
|
||||
'hide_empty' => ! $show_empty,
|
||||
'fields' => 'all',
|
||||
'name__like' => $search_text,
|
||||
);
|
||||
@@ -1780,6 +1814,7 @@ class WC_AJAX {
|
||||
foreach ( $terms as $term ) {
|
||||
$term->formatted_name = '';
|
||||
|
||||
$ancestors = array();
|
||||
if ( $term->parent ) {
|
||||
$ancestors = array_reverse( get_ancestors( $term->term_id, 'product_cat' ) );
|
||||
foreach ( $ancestors as $ancestor ) {
|
||||
@@ -1790,6 +1825,7 @@ class WC_AJAX {
|
||||
}
|
||||
}
|
||||
|
||||
$term->parents = $ancestors;
|
||||
$term->formatted_name .= $term->name . ' (' . $term->count . ')';
|
||||
$found_categories[ $term->term_id ] = $term;
|
||||
}
|
||||
@@ -1798,6 +1834,75 @@ class WC_AJAX {
|
||||
wp_send_json( apply_filters( 'woocommerce_json_search_found_categories', $found_categories ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for categories and return json.
|
||||
*/
|
||||
public static function json_search_categories_tree() {
|
||||
ob_start();
|
||||
|
||||
check_ajax_referer( 'search-categories', 'security' );
|
||||
|
||||
if ( ! current_user_can( 'edit_products' ) ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
|
||||
$search_text = isset( $_GET['term'] ) ? wc_clean( wp_unslash( $_GET['term'] ) ) : '';
|
||||
$number = isset( $_GET['number'] ) ? absint( $_GET['number'] ) : 50;
|
||||
|
||||
$args = array(
|
||||
'taxonomy' => array( 'product_cat' ),
|
||||
'orderby' => 'name',
|
||||
'order' => 'ASC',
|
||||
'hide_empty' => false,
|
||||
'fields' => 'all',
|
||||
'number' => $number,
|
||||
'name__like' => $search_text,
|
||||
);
|
||||
|
||||
$terms = get_terms( $args );
|
||||
|
||||
$terms_map = array();
|
||||
|
||||
if ( $terms ) {
|
||||
foreach ( $terms as $term ) {
|
||||
$terms_map[ $term->term_id ] = $term;
|
||||
|
||||
if ( $term->parent ) {
|
||||
$ancestors = get_ancestors( $term->term_id, 'product_cat' );
|
||||
$current_child = $term;
|
||||
foreach ( $ancestors as $ancestor ) {
|
||||
if ( ! isset( $terms_map[ $ancestor ] ) ) {
|
||||
$ancestor_term = get_term( $ancestor, 'product_cat' );
|
||||
$terms_map[ $ancestor ] = $ancestor_term;
|
||||
}
|
||||
if ( ! $terms_map[ $ancestor ]->children ) {
|
||||
$terms_map[ $ancestor ]->children = array();
|
||||
}
|
||||
$item_exists = count(
|
||||
array_filter(
|
||||
$terms_map[ $ancestor ]->children,
|
||||
function( $term ) use ( $current_child ) {
|
||||
return $term->term_id === $current_child->term_id;
|
||||
}
|
||||
)
|
||||
) === 1;
|
||||
if ( ! $item_exists ) {
|
||||
$terms_map[ $ancestor ]->children[] = $current_child;
|
||||
}
|
||||
$current_child = $terms_map[ $ancestor ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$parent_terms = array_filter(
|
||||
array_values( $terms_map ),
|
||||
function( $term ) {
|
||||
return 0 === $term->parent;
|
||||
}
|
||||
);
|
||||
wp_send_json( apply_filters( 'woocommerce_json_search_found_categories', $parent_terms ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for taxonomy terms and return json.
|
||||
*/
|
||||
@@ -1814,11 +1919,12 @@ class WC_AJAX {
|
||||
$limit = isset( $_GET['limit'] ) ? absint( wp_unslash( $_GET['limit'] ) ) : null;
|
||||
$taxonomy = isset( $_GET['taxonomy'] ) ? wc_clean( wp_unslash( $_GET['taxonomy'] ) ) : '';
|
||||
$orderby = isset( $_GET['orderby'] ) ? wc_clean( wp_unslash( $_GET['orderby'] ) ) : 'name';
|
||||
$order = isset( $_GET['order'] ) ? wc_clean( wp_unslash( $_GET['order'] ) ) : 'ASC';
|
||||
|
||||
$args = array(
|
||||
'taxonomy' => $taxonomy,
|
||||
'orderby' => $orderby,
|
||||
'order' => 'ASC',
|
||||
'order' => $order,
|
||||
'hide_empty' => false,
|
||||
'fields' => 'all',
|
||||
'number' => $limit,
|
||||
@@ -2899,6 +3005,18 @@ class WC_AJAX {
|
||||
// That's fine, it's not in the database anyways. NEXT!
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Notify that a non-option setting has been deleted.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => 'shipping_zone',
|
||||
'action' => 'delete',
|
||||
)
|
||||
);
|
||||
WC_Shipping_Zones::delete_zone( $zone_id );
|
||||
continue;
|
||||
}
|
||||
@@ -2915,13 +3033,31 @@ class WC_AJAX {
|
||||
$zone = new WC_Shipping_Zone( $zone_data['zone_id'] );
|
||||
|
||||
if ( isset( $zone_data['zone_order'] ) ) {
|
||||
/**
|
||||
* Notify that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => 'zone_order',
|
||||
)
|
||||
);
|
||||
$zone->set_zone_order( $zone_data['zone_order'] );
|
||||
}
|
||||
|
||||
$zone->save();
|
||||
}
|
||||
}
|
||||
|
||||
global $current_tab;
|
||||
$current_tab = 'shipping';
|
||||
/**
|
||||
* Completes the saving process for options.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_options' );
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'zones' => WC_Shipping_Zones::get_zones( 'json' ),
|
||||
@@ -2949,10 +3085,46 @@ class WC_AJAX {
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$zone_id = wc_clean( wp_unslash( $_POST['zone_id'] ) );
|
||||
$zone = new WC_Shipping_Zone( $zone_id );
|
||||
$zone_id = wc_clean( wp_unslash( $_POST['zone_id'] ) );
|
||||
$zone = new WC_Shipping_Zone( $zone_id );
|
||||
// A shipping zone can be created here if the user is adding a method without first saving the shipping zone.
|
||||
if ( '' === $zone_id ) {
|
||||
/**
|
||||
* Notified that a non-option setting has been added.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => 'shipping_zone',
|
||||
'action' => 'add',
|
||||
)
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Notify that a non-option setting has been added.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => 'zone_method',
|
||||
'action' => 'add',
|
||||
)
|
||||
);
|
||||
$instance_id = $zone->add_shipping_method( wc_clean( wp_unslash( $_POST['method_id'] ) ) );
|
||||
|
||||
global $current_tab;
|
||||
$current_tab = 'shipping';
|
||||
/**
|
||||
* Completes the saving process for options.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_options' );
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'instance_id' => $instance_id,
|
||||
@@ -2963,6 +3135,63 @@ class WC_AJAX {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle submissions from assets/js/wc-shipping-zone-methods.js Backbone model.
|
||||
*/
|
||||
public static function shipping_zone_remove_method() {
|
||||
if ( ! isset( $_POST['wc_shipping_zones_nonce'], $_POST['instance_id'], $_POST['zone_id'] ) ) {
|
||||
wp_send_json_error( 'missing_fields' );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if ( ! wp_verify_nonce( wp_unslash( $_POST['wc_shipping_zones_nonce'] ), 'wc_shipping_zones_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
wp_send_json_error( 'bad_nonce' );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// Check User Caps.
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( 'missing_capabilities' );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$zone_id = wc_clean( wp_unslash( $_POST['zone_id'] ) );
|
||||
$zone = new WC_Shipping_Zone( $zone_id );
|
||||
$instance_id = wc_clean( wp_unslash( $_POST['instance_id'] ) );
|
||||
|
||||
/**
|
||||
* Notify that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => $instance_id,
|
||||
)
|
||||
);
|
||||
if ( ! $zone->delete_shipping_method( $instance_id ) ) {
|
||||
wp_send_json_error( 'missing_shipping_method_instance_id' );
|
||||
wp_die();
|
||||
}
|
||||
|
||||
global $current_tab;
|
||||
$current_tab = 'shipping';
|
||||
/**
|
||||
* Completes the saving process for options.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_options' );
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'instance_id' => $instance_id,
|
||||
'methods' => $zone->get_shipping_methods( false, 'json' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle submissions from assets/js/wc-shipping-zone-methods.js Backbone model.
|
||||
*/
|
||||
@@ -2986,13 +3215,40 @@ class WC_AJAX {
|
||||
|
||||
$zone_id = wc_clean( wp_unslash( $_POST['zone_id'] ) );
|
||||
$zone = new WC_Shipping_Zone( $zone_id );
|
||||
// A shipping zone can be created here if the user is adding a method without first saving the shipping zone.
|
||||
if ( '' === $zone_id ) {
|
||||
/**
|
||||
* Notifies that a non-option setting has been added.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => 'shipping_zone',
|
||||
'action' => 'add',
|
||||
)
|
||||
);
|
||||
}
|
||||
$changes = wp_unslash( $_POST['changes'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
|
||||
if ( isset( $changes['zone_name'] ) ) {
|
||||
/**
|
||||
* Notifies that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'zone_name' ) );
|
||||
$zone->set_zone_name( wc_clean( $changes['zone_name'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $changes['zone_locations'] ) ) {
|
||||
/**
|
||||
* Notifies that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'zone_locations' ) );
|
||||
$zone->clear_locations( array( 'state', 'country', 'continent' ) );
|
||||
$locations = array_filter( array_map( 'wc_clean', (array) $changes['zone_locations'] ) );
|
||||
foreach ( $locations as $location ) {
|
||||
@@ -3013,6 +3269,12 @@ class WC_AJAX {
|
||||
}
|
||||
|
||||
if ( isset( $changes['zone_postcodes'] ) ) {
|
||||
/**
|
||||
* Notifies that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'zone_postcodes' ) );
|
||||
$zone->clear_locations( 'postcode' );
|
||||
$postcodes = array_filter( array_map( 'strtoupper', array_map( 'wc_clean', explode( "\n", $changes['zone_postcodes'] ) ) ) );
|
||||
foreach ( $postcodes as $postcode ) {
|
||||
@@ -3029,6 +3291,18 @@ class WC_AJAX {
|
||||
$option_key = $shipping_method->get_instance_option_key();
|
||||
if ( $wpdb->delete( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'instance_id' => $instance_id ) ) ) {
|
||||
delete_option( $option_key );
|
||||
/**
|
||||
* Notifies that a non-option setting has been deleted.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => 'zone_method',
|
||||
'action' => 'delete',
|
||||
)
|
||||
);
|
||||
do_action( 'woocommerce_shipping_zone_method_deleted', $instance_id, $method_id, $zone_id );
|
||||
}
|
||||
continue;
|
||||
@@ -3043,10 +3317,22 @@ class WC_AJAX {
|
||||
);
|
||||
|
||||
if ( isset( $method_data['method_order'] ) ) {
|
||||
/**
|
||||
* Notifies that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'zone_methods_order' ) );
|
||||
$wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'method_order' => absint( $method_data['method_order'] ) ), array( 'instance_id' => absint( $instance_id ) ) );
|
||||
}
|
||||
|
||||
if ( isset( $method_data['enabled'] ) ) {
|
||||
/**
|
||||
* Notifies that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'zone_methods_enabled' ) );
|
||||
$is_enabled = absint( 'yes' === $method_data['enabled'] );
|
||||
if ( $wpdb->update( "{$wpdb->prefix}woocommerce_shipping_zone_methods", array( 'is_enabled' => $is_enabled ), array( 'instance_id' => absint( $instance_id ) ) ) ) {
|
||||
do_action( 'woocommerce_shipping_zone_method_status_toggled', $instance_id, $method_id, $zone_id, $is_enabled );
|
||||
@@ -3057,6 +3343,15 @@ class WC_AJAX {
|
||||
|
||||
$zone->save();
|
||||
|
||||
global $current_tab;
|
||||
$current_tab = 'shipping';
|
||||
/**
|
||||
* Completes the saving process for options.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_options' );
|
||||
|
||||
wp_send_json_success(
|
||||
array(
|
||||
'zone_id' => $zone->get_id(),
|
||||
@@ -3088,7 +3383,22 @@ class WC_AJAX {
|
||||
$instance_id = absint( $_POST['instance_id'] );
|
||||
$zone = WC_Shipping_Zones::get_zone_by( 'instance_id', $instance_id );
|
||||
$shipping_method = WC_Shipping_Zones::get_shipping_method( $instance_id );
|
||||
/**
|
||||
* Notify that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'zone_method_settings' ) );
|
||||
$shipping_method->set_post_data( wp_unslash( $_POST['data'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
|
||||
global $current_tab;
|
||||
$current_tab = 'shipping';
|
||||
/**
|
||||
* Completes the saving process for options.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_options' );
|
||||
$shipping_method->process_admin_options();
|
||||
|
||||
WC_Cache_Helper::get_transient_version( 'shipping', true );
|
||||
@@ -3133,6 +3443,18 @@ class WC_AJAX {
|
||||
// That's fine, it's not in the database anyways. NEXT!
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Notifies that a non-option setting has been deleted.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => 'shipping_class',
|
||||
'action' => 'delete',
|
||||
)
|
||||
);
|
||||
wp_delete_term( $term_id, 'product_shipping_class' );
|
||||
continue;
|
||||
}
|
||||
@@ -3140,14 +3462,32 @@ class WC_AJAX {
|
||||
$update_args = array();
|
||||
|
||||
if ( isset( $data['name'] ) ) {
|
||||
/**
|
||||
* Notify that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'shipping_class_name' ) );
|
||||
$update_args['name'] = wc_clean( $data['name'] );
|
||||
}
|
||||
|
||||
if ( isset( $data['slug'] ) ) {
|
||||
/**
|
||||
* Notify that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'shipping_class_slug' ) );
|
||||
$update_args['slug'] = wc_clean( $data['slug'] );
|
||||
}
|
||||
|
||||
if ( isset( $data['description'] ) ) {
|
||||
/**
|
||||
* Notify that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'shipping_class_description' ) );
|
||||
$update_args['description'] = wc_clean( $data['description'] );
|
||||
}
|
||||
|
||||
@@ -3156,15 +3496,42 @@ class WC_AJAX {
|
||||
if ( empty( $update_args['name'] ) ) {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* Notifies that a non-option setting has been added.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action(
|
||||
'woocommerce_update_non_option_setting',
|
||||
array(
|
||||
'id' => 'shipping_class',
|
||||
'action' => 'add',
|
||||
)
|
||||
);
|
||||
$inserted_term = wp_insert_term( $update_args['name'], 'product_shipping_class', $update_args );
|
||||
$term_id = is_wp_error( $inserted_term ) ? 0 : $inserted_term['term_id'];
|
||||
} else {
|
||||
/**
|
||||
* Notifies that a non-option setting has been updated.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_non_option_setting', array( 'id' => 'shipping_class' ) );
|
||||
wp_update_term( $term_id, 'product_shipping_class', $update_args );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_shipping_classes_save_class', $term_id, $data );
|
||||
}
|
||||
|
||||
global $current_tab, $current_section;
|
||||
$current_tab = 'shipping';
|
||||
$current_section = 'classes';
|
||||
/**
|
||||
* Completes the saving process for options.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
do_action( 'woocommerce_update_options' );
|
||||
$wc_shipping = WC_Shipping::instance();
|
||||
|
||||
wp_send_json_success(
|
||||
@@ -3217,7 +3584,6 @@ class WC_AJAX {
|
||||
// Disable the gateway.
|
||||
$gateway->update_option( 'enabled', 'no' );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_update_options' );
|
||||
wp_send_json_success( ! wc_string_to_bool( $enabled ) );
|
||||
wp_die();
|
||||
@@ -3243,6 +3609,31 @@ class WC_AJAX {
|
||||
private static function order_delete_meta() : void {
|
||||
wc_get_container()->get( CustomMetaBox::class )->delete_meta_ajax();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked to 'heartbeat_received' on the edit order page to refresh the lock on an order being edited by the current user.
|
||||
*
|
||||
* @param array $response The heartbeat response to be sent.
|
||||
* @param array $data Data sent through the heartbeat.
|
||||
* @return array Response to be sent.
|
||||
*/
|
||||
private static function order_refresh_lock( $response, $data ) {
|
||||
return wc_get_container()->get( Automattic\WooCommerce\Internal\Admin\Orders\EditLock::class )->refresh_lock_ajax( $response, $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked to 'heartbeat_received' on the orders screen to refresh the locked status of orders in the list table.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*
|
||||
* @param array $response The heartbeat response to be sent.
|
||||
* @param array $data Data sent through the heartbeat.
|
||||
* @return array Response to be sent.
|
||||
*/
|
||||
private static function check_locked_orders( $response, $data ) {
|
||||
return wc_get_container()->get( Automattic\WooCommerce\Internal\Admin\Orders\EditLock::class )->check_locked_orders_ajax( $response, $data );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WC_AJAX::init();
|
||||
|
||||
@@ -117,6 +117,13 @@ final class WC_Cart_Totals {
|
||||
'discounts_total' => 0,
|
||||
);
|
||||
|
||||
/**
|
||||
* Cache of tax rates for a given tax class.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $item_tax_rates;
|
||||
|
||||
/**
|
||||
* Sets up the items provided, and calculate totals.
|
||||
*
|
||||
|
||||
@@ -822,6 +822,11 @@ class WC_Cart extends WC_Legacy_Cart {
|
||||
if ( $values['quantity'] > 0 ) {
|
||||
$cross_sells = array_merge( $values['data']->get_cross_sell_ids(), $cross_sells );
|
||||
$in_cart[] = $values['product_id'];
|
||||
|
||||
// Add variations to the in cart array.
|
||||
if ( $values['data']->is_type( 'variation' ) ) {
|
||||
$in_cart[] = $values['variation_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,16 @@ class WC_Countries {
|
||||
*/
|
||||
public $address_formats = array();
|
||||
|
||||
/**
|
||||
* Cache of geographical regions.
|
||||
*
|
||||
* Only to be used by the get_* and load_* methods, as other methods may expect the regions to be
|
||||
* loaded on demand.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $geo_cache = array();
|
||||
|
||||
/**
|
||||
* Auto-load in-accessible properties on demand.
|
||||
*
|
||||
@@ -38,6 +48,8 @@ class WC_Countries {
|
||||
return $this->get_countries();
|
||||
} elseif ( 'states' === $key ) {
|
||||
return $this->get_states();
|
||||
} elseif ( 'continents' === $key ) {
|
||||
return $this->get_continents();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,14 +59,21 @@ class WC_Countries {
|
||||
* @return array
|
||||
*/
|
||||
public function get_countries() {
|
||||
if ( empty( $this->countries ) ) {
|
||||
$this->countries = apply_filters( 'woocommerce_countries', include WC()->plugin_path() . '/i18n/countries.php' );
|
||||
if ( empty( $this->geo_cache['countries'] ) ) {
|
||||
/**
|
||||
* Allows filtering of the list of countries in WC.
|
||||
*
|
||||
* @since 1.5.3
|
||||
*
|
||||
* @param array $countries
|
||||
*/
|
||||
$this->geo_cache['countries'] = apply_filters( 'woocommerce_countries', include WC()->plugin_path() . '/i18n/countries.php' );
|
||||
if ( apply_filters( 'woocommerce_sort_countries', true ) ) {
|
||||
wc_asort_by_locale( $this->countries );
|
||||
wc_asort_by_locale( $this->geo_cache['countries'] );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->countries;
|
||||
return $this->geo_cache['countries'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,11 +93,18 @@ class WC_Countries {
|
||||
* @return array
|
||||
*/
|
||||
public function get_continents() {
|
||||
if ( empty( $this->continents ) ) {
|
||||
$this->continents = apply_filters( 'woocommerce_continents', include WC()->plugin_path() . '/i18n/continents.php' );
|
||||
if ( empty( $this->geo_cache['continents'] ) ) {
|
||||
/**
|
||||
* Allows filtering of continents in WC.
|
||||
*
|
||||
* @since 2.6.0
|
||||
*
|
||||
* @param array[array] $continents
|
||||
*/
|
||||
$this->geo_cache['continents'] = apply_filters( 'woocommerce_continents', include WC()->plugin_path() . '/i18n/continents.php' );
|
||||
}
|
||||
|
||||
return $this->continents;
|
||||
return $this->geo_cache['continents'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,8 +180,16 @@ class WC_Countries {
|
||||
public function load_country_states() {
|
||||
global $states;
|
||||
|
||||
$states = include WC()->plugin_path() . '/i18n/states.php';
|
||||
$this->states = apply_filters( 'woocommerce_states', $states );
|
||||
$states = include WC()->plugin_path() . '/i18n/states.php';
|
||||
|
||||
/**
|
||||
* Allows filtering of country states in WC.
|
||||
*
|
||||
* @since 1.5.3
|
||||
*
|
||||
* @param array $states
|
||||
*/
|
||||
$this->geo_cache['states'] = apply_filters( 'woocommerce_states', $states );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,14 +199,21 @@ class WC_Countries {
|
||||
* @return false|array of states
|
||||
*/
|
||||
public function get_states( $cc = null ) {
|
||||
if ( ! isset( $this->states ) ) {
|
||||
$this->states = apply_filters( 'woocommerce_states', include WC()->plugin_path() . '/i18n/states.php' );
|
||||
if ( ! isset( $this->geo_cache['states'] ) ) {
|
||||
/**
|
||||
* Allows filtering of country states in WC.
|
||||
*
|
||||
* @since 1.5.3
|
||||
*
|
||||
* @param array $states
|
||||
*/
|
||||
$this->geo_cache['states'] = apply_filters( 'woocommerce_states', include WC()->plugin_path() . '/i18n/states.php' );
|
||||
}
|
||||
|
||||
if ( ! is_null( $cc ) ) {
|
||||
return isset( $this->states[ $cc ] ) ? $this->states[ $cc ] : false;
|
||||
return isset( $this->geo_cache['states'][ $cc ] ) ? $this->geo_cache['states'][ $cc ] : false;
|
||||
} else {
|
||||
return $this->states;
|
||||
return $this->geo_cache['states'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,6 +515,11 @@ class WC_Countries {
|
||||
foreach ( $this->countries as $key => $value ) {
|
||||
$states = $this->get_states( $key );
|
||||
if ( $states ) {
|
||||
// Maybe default the selected state as the first one.
|
||||
if ( $selected_country === $key && '*' === $selected_state ) {
|
||||
$selected_state = key( $states ) ?? '*';
|
||||
}
|
||||
|
||||
echo '<optgroup label="' . esc_attr( $value ) . '">';
|
||||
foreach ( $states as $state_key => $state_value ) {
|
||||
echo '<option value="' . esc_attr( $key ) . ':' . esc_attr( $state_key ) . '"';
|
||||
@@ -546,7 +592,7 @@ class WC_Countries {
|
||||
'TW' => "{company}\n{last_name} {first_name}\n{address_1}\n{address_2}\n{state}, {city} {postcode}\n{country}",
|
||||
'UG' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}, {country}",
|
||||
'US' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}, {state_code} {postcode}\n{country}",
|
||||
'VN' => "{name}\n{company}\n{address_1}\n{city}\n{country}",
|
||||
'VN' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1024,6 +1070,12 @@ class WC_Countries {
|
||||
'hidden' => true,
|
||||
),
|
||||
),
|
||||
'ET' => array(
|
||||
'state' => array(
|
||||
'required' => false,
|
||||
'hidden' => true,
|
||||
),
|
||||
),
|
||||
'FI' => array(
|
||||
'postcode' => array(
|
||||
'priority' => 65,
|
||||
@@ -1234,6 +1286,16 @@ class WC_Countries {
|
||||
'priority' => 69,
|
||||
),
|
||||
),
|
||||
'KN' => array(
|
||||
'postcode' => array(
|
||||
'required' => false,
|
||||
'label' => __( 'Postal code', 'woocommerce' ),
|
||||
),
|
||||
'state' => array(
|
||||
'required' => true,
|
||||
'label' => __( 'Parish', 'woocommerce' ),
|
||||
),
|
||||
),
|
||||
'KR' => array(
|
||||
'state' => array(
|
||||
'required' => false,
|
||||
@@ -1393,6 +1455,12 @@ class WC_Countries {
|
||||
'required' => false,
|
||||
),
|
||||
),
|
||||
'RW' => array(
|
||||
'state' => array(
|
||||
'required' => false,
|
||||
'hidden' => true,
|
||||
),
|
||||
),
|
||||
'SG' => array(
|
||||
'state' => array(
|
||||
'required' => false,
|
||||
@@ -1573,7 +1641,7 @@ class WC_Countries {
|
||||
// Default Locale Can be filtered to override fields in get_address_fields(). Countries with no specific locale will use default.
|
||||
$this->locale['default'] = apply_filters( 'woocommerce_get_country_locale_default', $this->get_default_address_fields() );
|
||||
|
||||
// Filter default AND shop base locales to allow overides via a single function. These will be used when changing countries on the checkout.
|
||||
// Filter default AND shop base locales to allow overrides via a single function. These will be used when changing countries on the checkout.
|
||||
if ( ! isset( $this->locale[ $this->get_base_country() ] ) ) {
|
||||
$this->locale[ $this->get_base_country() ] = $this->locale['default'];
|
||||
}
|
||||
|
||||
@@ -698,7 +698,7 @@ class WC_Coupon extends WC_Legacy_Coupon {
|
||||
* Set the minimum spend amount.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param float $amount Minium amount.
|
||||
* @param float $amount Minimum amount.
|
||||
*/
|
||||
public function set_minimum_amount( $amount ) {
|
||||
$this->set_prop( 'minimum_amount', wc_format_decimal( $amount ) );
|
||||
|
||||
@@ -929,10 +929,17 @@ class WC_Form_Handler {
|
||||
* @throws Exception On login error.
|
||||
*/
|
||||
public static function process_login() {
|
||||
// The global form-login.php template used `_wpnonce` in template versions < 3.3.0.
|
||||
$nonce_value = wc_get_var( $_REQUEST['woocommerce-login-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
|
||||
|
||||
if ( isset( $_POST['login'], $_POST['username'], $_POST['password'] ) && wp_verify_nonce( $nonce_value, 'woocommerce-login' ) ) {
|
||||
static $valid_nonce = null;
|
||||
|
||||
if ( null === $valid_nonce ) {
|
||||
// The global form-login.php template used `_wpnonce` in template versions < 3.3.0.
|
||||
$nonce_value = wc_get_var( $_REQUEST['woocommerce-login-nonce'], wc_get_var( $_REQUEST['_wpnonce'], '' ) ); // @codingStandardsIgnoreLine.
|
||||
|
||||
$valid_nonce = wp_verify_nonce( $nonce_value, 'woocommerce-login' );
|
||||
}
|
||||
|
||||
if ( isset( $_POST['login'], $_POST['username'], $_POST['password'] ) && $valid_nonce ) {
|
||||
|
||||
try {
|
||||
$creds = array(
|
||||
@@ -961,7 +968,7 @@ class WC_Form_Handler {
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the login.
|
||||
// Peform the login.
|
||||
$user = wp_signon( apply_filters( 'woocommerce_login_credentials', $creds ), is_ssl() );
|
||||
|
||||
if ( is_wp_error( $user ) ) {
|
||||
@@ -976,7 +983,9 @@ class WC_Form_Handler {
|
||||
$redirect = wc_get_page_permalink( 'myaccount' );
|
||||
}
|
||||
|
||||
wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_login_redirect', remove_query_arg( 'wc_error', $redirect ), $user ), wc_get_page_permalink( 'myaccount' ) ) ); // phpcs:ignore
|
||||
$redirect = remove_query_arg( array( 'wc_error', 'password-reset' ), $redirect );
|
||||
|
||||
wp_redirect( wp_validate_redirect( apply_filters( 'woocommerce_login_redirect', $redirect, $user ), wc_get_page_permalink( 'myaccount' ) ) ); // phpcs:ignore
|
||||
exit;
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
|
||||
@@ -62,7 +62,7 @@ class WC_Frontend_Scripts {
|
||||
*
|
||||
* @since 2.1.0
|
||||
* @param array List of default WooCommerce styles.
|
||||
* @retrun array List of styles to enqueue.
|
||||
* @return array List of styles to enqueue.
|
||||
*/
|
||||
$styles = apply_filters(
|
||||
'woocommerce_enqueue_styles',
|
||||
@@ -430,7 +430,6 @@ class WC_Frontend_Scripts {
|
||||
|
||||
// Global frontend scripts.
|
||||
self::enqueue_script( 'woocommerce' );
|
||||
self::enqueue_script( 'wc-cart-fragments' );
|
||||
|
||||
// CSS Styles.
|
||||
$enqueue_styles = self::get_styles();
|
||||
|
||||
@@ -59,7 +59,7 @@ class WC_HTTPS {
|
||||
if ( is_array( $content ) ) {
|
||||
$content = array_map( 'WC_HTTPS::force_https_url', $content );
|
||||
} else {
|
||||
$content = str_replace( 'http:', 'https:', $content );
|
||||
$content = str_replace( 'http:', 'https:', (string) $content );
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
|
||||
@@ -8,11 +8,16 @@
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Admin\Notes\Notes;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
use Automattic\WooCommerce\Internal\Features\FeaturesController;
|
||||
use Automattic\WooCommerce\Internal\ProductAttributesLookup\DataRegenerator;
|
||||
use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Download_Directories;
|
||||
use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Synchronize as Download_Directories_Sync;
|
||||
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
|
||||
use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper as WCConnectionHelper;
|
||||
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
@@ -20,6 +25,7 @@ defined( 'ABSPATH' ) || exit;
|
||||
* WC_Install Class.
|
||||
*/
|
||||
class WC_Install {
|
||||
use AccessiblePrivateMethods;
|
||||
|
||||
/**
|
||||
* DB updates and callbacks that need to be run per version.
|
||||
@@ -232,8 +238,18 @@ class WC_Install {
|
||||
'7.7.0' => array(
|
||||
'wc_update_770_remove_multichannel_marketing_feature_options',
|
||||
),
|
||||
'8.1.0' => array(
|
||||
'wc_update_810_migrate_transactional_metadata_for_hpos',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Option name used to track new installations of WooCommerce.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const NEWLY_INSTALLED_OPTION = 'woocommerce_newly_installed';
|
||||
|
||||
/**
|
||||
* Hook in tabs.
|
||||
*/
|
||||
@@ -250,6 +266,26 @@ class WC_Install {
|
||||
add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
|
||||
add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
|
||||
add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
|
||||
self::add_action( 'admin_init', array( __CLASS__, 'newly_installed' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger `woocommerce_newly_installed` action for new installations.
|
||||
*
|
||||
* @since 8.0.0
|
||||
*/
|
||||
private static function newly_installed() {
|
||||
if ( 'yes' === get_option( self::NEWLY_INSTALLED_OPTION, false ) ) {
|
||||
/**
|
||||
* Run when WooCommerce has been installed for the first time.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*/
|
||||
do_action( 'woocommerce_newly_installed' );
|
||||
do_action_deprecated( 'woocommerce_admin_newly_installed', array(), '6.5.0', 'woocommerce_newly_installed' );
|
||||
|
||||
update_option( self::NEWLY_INSTALLED_OPTION, 'no' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,16 +306,6 @@ class WC_Install {
|
||||
*/
|
||||
do_action( 'woocommerce_updated' );
|
||||
do_action_deprecated( 'woocommerce_admin_updated', array(), $wc_code_version, 'woocommerce_updated' );
|
||||
// If there is no woocommerce_version option, consider it as a new install.
|
||||
if ( ! $wc_version ) {
|
||||
/**
|
||||
* Run when WooCommerce has been installed for the first time.
|
||||
*
|
||||
* @since 6.5.0
|
||||
*/
|
||||
do_action( 'woocommerce_newly_installed' );
|
||||
do_action_deprecated( 'woocommerce_admin_newly_installed', array(), $wc_code_version, 'woocommerce_newly_installed' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,6 +417,10 @@ class WC_Install {
|
||||
set_transient( 'wc_installing', 'yes', MINUTE_IN_SECONDS * 10 );
|
||||
wc_maybe_define_constant( 'WC_INSTALLING', true );
|
||||
|
||||
if ( self::is_new_install() && ! get_option( self::NEWLY_INSTALLED_OPTION, false ) ) {
|
||||
update_option( self::NEWLY_INSTALLED_OPTION, 'yes' );
|
||||
}
|
||||
|
||||
WC()->wpdb_table_fix();
|
||||
self::remove_admin_notices();
|
||||
self::create_tables();
|
||||
@@ -457,9 +487,21 @@ class WC_Install {
|
||||
self::create_tables();
|
||||
}
|
||||
|
||||
$schema = self::get_schema();
|
||||
|
||||
$feature_controller = wc_get_container()->get( FeaturesController::class );
|
||||
if (
|
||||
$feature_controller->feature_is_enabled( DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION )
|
||||
|| $feature_controller->feature_is_enabled( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION )
|
||||
) {
|
||||
$schema .= wc_get_container()
|
||||
->get( OrdersTableDataStore::class )
|
||||
->get_database_schema();
|
||||
}
|
||||
|
||||
$missing_tables = wc_get_container()
|
||||
->get( DatabaseUtil::class )
|
||||
->get_missing_tables( self::get_schema() );
|
||||
->get_missing_tables( $schema );
|
||||
|
||||
if ( 0 < count( $missing_tables ) ) {
|
||||
if ( $modify_notice ) {
|
||||
@@ -710,6 +752,9 @@ class WC_Install {
|
||||
* Create pages that the plugin relies on, storing page IDs in variables.
|
||||
*/
|
||||
public static function create_pages() {
|
||||
// Set the locale to the store locale to ensure pages are created in the correct language.
|
||||
wc_switch_to_site_locale();
|
||||
|
||||
include_once dirname( __FILE__ ) . '/admin/wc-admin-functions.php';
|
||||
|
||||
/**
|
||||
@@ -780,6 +825,9 @@ class WC_Install {
|
||||
! empty( $page['post_status'] ) ? $page['post_status'] : 'publish'
|
||||
);
|
||||
}
|
||||
|
||||
// Restore the locale to the default locale.
|
||||
wc_restore_locale();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -885,10 +933,42 @@ class WC_Install {
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $obsolete_notes_names as $obsolete_notes_name ) {
|
||||
$wpdb->delete( $wpdb->prefix . 'wc_admin_notes', array( 'name' => $obsolete_notes_name ) );
|
||||
$wpdb->delete( $wpdb->prefix . 'wc_admin_note_actions', array( 'name' => $obsolete_notes_name ) );
|
||||
$note_names_placeholder = substr( str_repeat( ',%s', count( $obsolete_notes_names ) ), 1 );
|
||||
|
||||
$note_ids = $wpdb->get_results(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Ignored for allowing interpolation in the IN statement.
|
||||
$wpdb->prepare(
|
||||
"SELECT note_id FROM {$wpdb->prefix}wc_admin_notes WHERE name IN ( $note_names_placeholder )",
|
||||
$obsolete_notes_names
|
||||
),
|
||||
ARRAY_N
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare.
|
||||
);
|
||||
|
||||
if ( ! $note_ids ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$note_ids = array_column( $note_ids, 0 );
|
||||
$note_ids_placeholder = substr( str_repeat( ',%d', count( $note_ids ) ), 1 );
|
||||
|
||||
$wpdb->query(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Ignored for allowing interpolation in the IN statement.
|
||||
$wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->prefix}wc_admin_notes WHERE note_id IN ( $note_ids_placeholder )",
|
||||
$note_ids
|
||||
)
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare.
|
||||
);
|
||||
|
||||
$wpdb->query(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Ignored for allowing interpolation in the IN statement.
|
||||
$wpdb->prepare(
|
||||
"DELETE FROM {$wpdb->prefix}wc_admin_note_actions WHERE note_id IN ( $note_ids_placeholder )",
|
||||
$note_ids
|
||||
)
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare.
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1081,15 +1161,15 @@ class WC_Install {
|
||||
$collate = $wpdb->get_charset_collate();
|
||||
}
|
||||
|
||||
/*
|
||||
* Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
|
||||
* As of WP 4.2, however, they moved to utf8mb4, which uses 4 bytes per character. This means that an index which
|
||||
* used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
|
||||
*/
|
||||
$max_index_length = 191;
|
||||
$max_index_length = wc_get_container()->get( DatabaseUtil::class )->get_max_index_length();
|
||||
|
||||
$product_attributes_lookup_table_creation_sql = wc_get_container()->get( DataRegenerator::class )->get_table_creation_sql();
|
||||
|
||||
$feature_controller = wc_get_container()->get( FeaturesController::class );
|
||||
$hpos_enabled =
|
||||
$feature_controller->feature_is_enabled( DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION ) || $feature_controller->feature_is_enabled( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION );
|
||||
$hpos_table_schema = $hpos_enabled ? wc_get_container()->get( OrdersTableDataStore::class )->get_database_schema() : '';
|
||||
|
||||
$tables = "
|
||||
CREATE TABLE {$wpdb->prefix}woocommerce_sessions (
|
||||
session_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
@@ -1433,6 +1513,7 @@ CREATE TABLE {$wpdb->prefix}wc_category_lookup (
|
||||
category_id bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (category_tree_id,category_id)
|
||||
) $collate;
|
||||
$hpos_table_schema;
|
||||
";
|
||||
|
||||
return $tables;
|
||||
@@ -1506,7 +1587,9 @@ CREATE TABLE {$wpdb->prefix}wc_category_lookup (
|
||||
$tables = self::get_tables();
|
||||
|
||||
foreach ( $tables as $table ) {
|
||||
$wpdb->query( "DROP TABLE IF EXISTS {$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$wpdb->query( "DROP TABLE IF EXISTS {$table}" );
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1532,7 +1615,7 @@ CREATE TABLE {$wpdb->prefix}wc_category_lookup (
|
||||
}
|
||||
|
||||
if ( ! isset( $wp_roles ) ) {
|
||||
$wp_roles = new WP_Roles(); // @codingStandardsIgnoreLine
|
||||
$wp_roles = new WP_Roles(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
}
|
||||
|
||||
// Dummy gettext calls to get strings in the catalog.
|
||||
@@ -1662,7 +1745,7 @@ CREATE TABLE {$wpdb->prefix}wc_category_lookup (
|
||||
}
|
||||
|
||||
if ( ! isset( $wp_roles ) ) {
|
||||
$wp_roles = new WP_Roles(); // @codingStandardsIgnoreLine
|
||||
$wp_roles = new WP_Roles(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
|
||||
}
|
||||
|
||||
$capabilities = self::get_core_capabilities();
|
||||
|
||||
@@ -49,6 +49,8 @@ class WC_Order_Refund extends WC_Abstract_Order {
|
||||
* @var array
|
||||
*/
|
||||
protected $legacy_datastore_props = array(
|
||||
'_refund_amount',
|
||||
'_refund_reason',
|
||||
'_refunded_by',
|
||||
'_refunded_payment',
|
||||
);
|
||||
|
||||
@@ -108,6 +108,14 @@ class WC_Order extends WC_Abstract_Order {
|
||||
'_new_order_email_sent',
|
||||
);
|
||||
|
||||
/**
|
||||
* Refunds for an order. Use {@see get_refunds()} instead.
|
||||
*
|
||||
* @deprecated 2.2.0
|
||||
* @var stdClass|WC_Order[]
|
||||
*/
|
||||
public $refunds;
|
||||
|
||||
/**
|
||||
* When a payment is complete this function is called.
|
||||
*
|
||||
@@ -798,7 +806,7 @@ class WC_Order extends WC_Abstract_Order {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get transaction d.
|
||||
* Get transaction id.
|
||||
*
|
||||
* @param string $context What the value is for. Valid values are view and edit.
|
||||
* @return string
|
||||
@@ -2278,4 +2286,13 @@ class WC_Order extends WC_Abstract_Order {
|
||||
public function is_created_via( $modus ) {
|
||||
return apply_filters( 'woocommerce_order_is_created_via', $modus === $this->get_created_via(), $this, $modus );
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to restore the specified order back to its original status (after having been trashed).
|
||||
*
|
||||
* @return bool If the operation was successful.
|
||||
*/
|
||||
public function untrash(): bool {
|
||||
return (bool) $this->data_store->untrash_order( $this );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,7 +381,14 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
||||
}
|
||||
|
||||
foreach ( $user_ids as $user_id ) {
|
||||
wp_delete_user( $user_id );
|
||||
wp_delete_user( $user_id, 0 );
|
||||
wc_get_logger()->info(
|
||||
sprintf(
|
||||
/* translators: %d user ID. */
|
||||
__( "User #%d was deleted by WooCommerce in accordance with the site's personal data retention settings. Any content belonging to that user has been retained but unassigned.", 'woocommerce' ),
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
$count ++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,20 @@ class WC_Product_Variable extends WC_Product {
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the aria-describedby description for the add to cart button.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function add_to_cart_aria_describedby() {
|
||||
/**
|
||||
* This filter is documented in includes/abstracts/abstract-wc-product.php.
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
return apply_filters( 'woocommerce_product_add_to_cart_aria_describedby', $this->is_purchasable() ? __( 'This product has multiple variants. The options may be chosen on the product page', 'woocommerce' ) : '', $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the add to cart button text.
|
||||
*
|
||||
|
||||
@@ -538,7 +538,7 @@ class WC_Query {
|
||||
// Store reference to this query.
|
||||
self::$product_query = $q;
|
||||
|
||||
// Additonal hooks to change WP Query.
|
||||
// Additional hooks to change WP Query.
|
||||
self::add_filter( 'posts_clauses', array( $this, 'product_query_post_clauses' ), 10, 2 );
|
||||
add_filter( 'the_posts', array( $this, 'handle_get_posts' ), 10, 2 );
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class WC_Regenerate_Images {
|
||||
add_action( 'admin_init', array( __CLASS__, 'regenerating_notice' ) );
|
||||
add_action( 'woocommerce_hide_regenerating_thumbnails_notice', array( __CLASS__, 'dismiss_regenerating_notice' ) );
|
||||
|
||||
// Regenerate thumbnails in the background after settings changes. Not ran on multisite to avoid multiple simultanious jobs.
|
||||
// Regenerate thumbnails in the background after settings changes. Not ran on multisite to avoid multiple simultaneous jobs.
|
||||
if ( ! is_multisite() ) {
|
||||
add_action( 'customize_save_after', array( __CLASS__, 'maybe_regenerate_images' ) );
|
||||
add_action( 'after_switch_theme', array( __CLASS__, 'maybe_regenerate_images' ) );
|
||||
|
||||
@@ -292,7 +292,13 @@ class WC_Session_Handler extends WC_Session {
|
||||
return false;
|
||||
}
|
||||
|
||||
list( $customer_id, $session_expiration, $session_expiring, $cookie_hash ) = explode( '||', $cookie_value );
|
||||
$parsed_cookie = explode( '||', $cookie_value );
|
||||
|
||||
if ( count( $parsed_cookie ) < 4 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
list( $customer_id, $session_expiration, $session_expiring, $cookie_hash ) = $parsed_cookie;
|
||||
|
||||
if ( empty( $customer_id ) ) {
|
||||
return false;
|
||||
|
||||
@@ -257,9 +257,15 @@ class WC_Structured_Data {
|
||||
);
|
||||
}
|
||||
|
||||
if ( $product->is_in_stock() ) {
|
||||
$stock_status_schema = ( 'onbackorder' === $product->get_stock_status() ) ? 'BackOrder' : 'InStock';
|
||||
} else {
|
||||
$stock_status_schema = 'OutOfStock';
|
||||
}
|
||||
|
||||
$markup_offer += array(
|
||||
'priceCurrency' => $currency,
|
||||
'availability' => 'http://schema.org/' . ( $product->is_in_stock() ? 'InStock' : 'OutOfStock' ),
|
||||
'availability' => 'http://schema.org/' . $stock_status_schema,
|
||||
'url' => $permalink,
|
||||
'seller' => array(
|
||||
'@type' => 'Organization',
|
||||
|
||||
@@ -165,6 +165,7 @@ class WC_Tax {
|
||||
*/
|
||||
public static function calc_exclusive_tax( $price, $rates ) {
|
||||
$taxes = array();
|
||||
$price = (float) $price;
|
||||
|
||||
if ( ! empty( $rates ) ) {
|
||||
foreach ( $rates as $key => $rate ) {
|
||||
@@ -172,13 +173,13 @@ class WC_Tax {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tax_amount = $price * ( $rate['rate'] / 100 );
|
||||
$tax_amount = $price * ( floatval( $rate['rate'] ) / 100 );
|
||||
$tax_amount = apply_filters( 'woocommerce_price_ex_tax_amount', $tax_amount, $key, $rate, $price ); // ADVANCED: Allow third parties to modify this rate.
|
||||
|
||||
if ( ! isset( $taxes[ $key ] ) ) {
|
||||
$taxes[ $key ] = $tax_amount;
|
||||
$taxes[ $key ] = (float) $tax_amount;
|
||||
} else {
|
||||
$taxes[ $key ] += $tax_amount;
|
||||
$taxes[ $key ] += (float) $tax_amount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,14 +190,14 @@ class WC_Tax {
|
||||
if ( 'no' === $rate['compound'] ) {
|
||||
continue;
|
||||
}
|
||||
$the_price_inc_tax = $price + ( $pre_compound_total );
|
||||
$tax_amount = $the_price_inc_tax * ( $rate['rate'] / 100 );
|
||||
$the_price_inc_tax = $price + $pre_compound_total;
|
||||
$tax_amount = $the_price_inc_tax * ( floatval( $rate['rate'] ) / 100 );
|
||||
$tax_amount = apply_filters( 'woocommerce_price_ex_tax_amount', $tax_amount, $key, $rate, $price, $the_price_inc_tax, $pre_compound_total ); // ADVANCED: Allow third parties to modify this rate.
|
||||
|
||||
if ( ! isset( $taxes[ $key ] ) ) {
|
||||
$taxes[ $key ] = $tax_amount;
|
||||
$taxes[ $key ] = (float) $tax_amount;
|
||||
} else {
|
||||
$taxes[ $key ] += $tax_amount;
|
||||
$taxes[ $key ] += (float) $tax_amount;
|
||||
}
|
||||
|
||||
$pre_compound_total = array_sum( $taxes );
|
||||
|
||||
@@ -250,25 +250,25 @@ class WC_Template_Loader {
|
||||
if ( is_product_taxonomy() ) {
|
||||
$object = get_queried_object();
|
||||
|
||||
$templates[] = 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php';
|
||||
$templates[] = 'taxonomy-' . $object->taxonomy . '.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-' . $object->taxonomy . '.php';
|
||||
|
||||
if ( taxonomy_is_product_attribute( $object->taxonomy ) ) {
|
||||
$templates[] = 'taxonomy-product_attribute.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-product_attribute.php';
|
||||
$templates[] = $default_file;
|
||||
} else {
|
||||
$templates[] = 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php';
|
||||
$templates[] = 'taxonomy-' . $object->taxonomy . '.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-' . $object->taxonomy . '.php';
|
||||
}
|
||||
|
||||
if ( is_tax( 'product_cat' ) || is_tax( 'product_tag' ) ) {
|
||||
$cs_taxonomy = str_replace( '_', '-', $object->taxonomy );
|
||||
$cs_default = str_replace( '_', '-', $default_file );
|
||||
$templates[] = 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-' . $cs_taxonomy . '-' . $object->slug . '.php';
|
||||
$templates[] = 'taxonomy-' . $object->taxonomy . '.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-' . $cs_taxonomy . '.php';
|
||||
$templates[] = $cs_default;
|
||||
}
|
||||
if ( is_tax( 'product_cat' ) || is_tax( 'product_tag' ) ) {
|
||||
$cs_taxonomy = str_replace( '_', '-', $object->taxonomy );
|
||||
$cs_default = str_replace( '_', '-', $default_file );
|
||||
$templates[] = 'taxonomy-' . $object->taxonomy . '-' . $object->slug . '.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-' . $cs_taxonomy . '-' . $object->slug . '.php';
|
||||
$templates[] = 'taxonomy-' . $object->taxonomy . '.php';
|
||||
$templates[] = WC()->template_path() . 'taxonomy-' . $cs_taxonomy . '.php';
|
||||
$templates[] = $cs_default;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,15 +11,20 @@
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
use Automattic\WooCommerce\Utilities\{ FeaturesUtil, OrderUtil, PluginUtil };
|
||||
use Automattic\WooCommerce\Internal\Utilities\BlocksUtil;
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
// phpcs:disable Squiz.Classes.ClassFileName.NoMatch, Squiz.Classes.ValidClassName.NotCamelCaps -- Backwards compatibility.
|
||||
/**
|
||||
* WooCommerce Tracker Class
|
||||
*/
|
||||
class WC_Tracker {
|
||||
|
||||
// phpcs:enable
|
||||
/**
|
||||
* URL to the WooThemes Tracker API endpoint.
|
||||
*
|
||||
@@ -30,7 +35,7 @@ class WC_Tracker {
|
||||
/**
|
||||
* Hook into cron event.
|
||||
*/
|
||||
public static function init() {
|
||||
public static function init() { // phpcs:ignore WooCommerce.Functions.InternalInjectionMethod.MissingFinal, WooCommerce.Functions.InternalInjectionMethod.MissingInternalTag -- Not an injection.
|
||||
add_action( 'woocommerce_tracker_send_event', array( __CLASS__, 'send_tracking_data' ) );
|
||||
}
|
||||
|
||||
@@ -45,10 +50,15 @@ class WC_Tracker {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter whether to send tracking data or not.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
if ( ! apply_filters( 'woocommerce_tracker_send_override', $override ) ) {
|
||||
// Send a maximum of once per week by default.
|
||||
$last_send = self::get_last_send_time();
|
||||
if ( $last_send && $last_send > apply_filters( 'woocommerce_tracker_last_send_interval', strtotime( '-1 week' ) ) ) {
|
||||
if ( $last_send && $last_send > apply_filters( 'woocommerce_tracker_last_send_interval', strtotime( '-1 week' ) ) ) { // phpcs:ignore
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -84,6 +94,11 @@ class WC_Tracker {
|
||||
* @return int|bool
|
||||
*/
|
||||
private static function get_last_send_time() {
|
||||
/**
|
||||
* Filter the last time tracking data was sent.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
return apply_filters( 'woocommerce_tracker_last_send_time', get_option( 'woocommerce_tracker_last_send', false ) );
|
||||
}
|
||||
|
||||
@@ -118,7 +133,12 @@ class WC_Tracker {
|
||||
$data = array();
|
||||
|
||||
// General site info.
|
||||
$data['url'] = home_url();
|
||||
$data['url'] = home_url();
|
||||
/**
|
||||
* Filter the admin email that's sent with data.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
$data['email'] = apply_filters( 'woocommerce_tracker_admin_email', get_option( 'admin_email' ) );
|
||||
$data['theme'] = self::get_theme_info();
|
||||
|
||||
@@ -134,7 +154,6 @@ class WC_Tracker {
|
||||
$data['inactive_plugins'] = $all_plugins['inactive_plugins'];
|
||||
|
||||
// Jetpack & WooCommerce Connect.
|
||||
|
||||
$data['jetpack_version'] = Constants::is_defined( 'JETPACK__VERSION' ) ? Constants::get_constant( 'JETPACK__VERSION' ) : 'none';
|
||||
$data['jetpack_connected'] = ( class_exists( 'Jetpack' ) && is_callable( 'Jetpack::is_active' ) && Jetpack::is_active() ) ? 'yes' : 'no';
|
||||
$data['jetpack_is_staging'] = self::is_jetpack_staging_site() ? 'yes' : 'no';
|
||||
@@ -158,6 +177,9 @@ class WC_Tracker {
|
||||
// Shipping method info.
|
||||
$data['shipping_methods'] = self::get_active_shipping_methods();
|
||||
|
||||
// Features.
|
||||
$data['enabled_features'] = self::get_enabled_features();
|
||||
|
||||
// Get all WooCommerce options info.
|
||||
$data['settings'] = self::get_all_woocommerce_options_values();
|
||||
|
||||
@@ -172,12 +194,21 @@ class WC_Tracker {
|
||||
$data['mini_cart_block'] = self::get_mini_cart_info();
|
||||
}
|
||||
|
||||
// WooCommerce Admin info.
|
||||
/**
|
||||
* Filter whether to disable admin tracking.
|
||||
*
|
||||
* @since 5.2.0
|
||||
*/
|
||||
$data['wc_admin_disabled'] = apply_filters( 'woocommerce_admin_disabled', false ) ? 'yes' : 'no';
|
||||
|
||||
// Mobile info.
|
||||
$data['wc_mobile_usage'] = self::get_woocommerce_mobile_usage();
|
||||
|
||||
/**
|
||||
* Filter the data that's sent with the tracker.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
return apply_filters( 'woocommerce_tracker_data', $data );
|
||||
}
|
||||
|
||||
@@ -212,6 +243,7 @@ class WC_Tracker {
|
||||
$memory = wc_let_to_num( WP_MEMORY_LIMIT );
|
||||
|
||||
if ( function_exists( 'memory_get_usage' ) ) {
|
||||
// phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- False positive.
|
||||
$system_memory = wc_let_to_num( @ini_get( 'memory_limit' ) );
|
||||
$memory = max( $memory, $system_memory );
|
||||
}
|
||||
@@ -280,27 +312,31 @@ class WC_Tracker {
|
||||
include ABSPATH . '/wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
$plugins = get_plugins();
|
||||
$plugins = wc_get_container()->get( LegacyProxy::class )->call_function( 'get_plugins' );
|
||||
$active_plugins_keys = get_option( 'active_plugins', array() );
|
||||
$active_plugins = array();
|
||||
|
||||
foreach ( $plugins as $k => $v ) {
|
||||
// Take care of formatting the data how we want it.
|
||||
$formatted = array();
|
||||
$formatted['name'] = strip_tags( $v['Name'] );
|
||||
$formatted['name'] = wp_strip_all_tags( $v['Name'] );
|
||||
if ( isset( $v['Version'] ) ) {
|
||||
$formatted['version'] = strip_tags( $v['Version'] );
|
||||
$formatted['version'] = wp_strip_all_tags( $v['Version'] );
|
||||
}
|
||||
if ( isset( $v['Author'] ) ) {
|
||||
$formatted['author'] = strip_tags( $v['Author'] );
|
||||
$formatted['author'] = wp_strip_all_tags( $v['Author'] );
|
||||
}
|
||||
if ( isset( $v['Network'] ) ) {
|
||||
$formatted['network'] = strip_tags( $v['Network'] );
|
||||
$formatted['network'] = wp_strip_all_tags( $v['Network'] );
|
||||
}
|
||||
if ( isset( $v['PluginURI'] ) ) {
|
||||
$formatted['plugin_uri'] = strip_tags( $v['PluginURI'] );
|
||||
$formatted['plugin_uri'] = wp_strip_all_tags( $v['PluginURI'] );
|
||||
}
|
||||
if ( in_array( $k, $active_plugins_keys ) ) {
|
||||
$formatted['feature_compatibility'] = array();
|
||||
if ( wc_get_container()->get( PluginUtil::class )->is_woocommerce_aware_plugin( $k ) ) {
|
||||
$formatted['feature_compatibility'] = array_filter( FeaturesUtil::get_compatible_features_for_plugin( $k ) );
|
||||
}
|
||||
if ( in_array( $k, $active_plugins_keys, true ) ) {
|
||||
// Remove active plugins from list so we can show active and inactive separately.
|
||||
unset( $plugins[ $k ] );
|
||||
$active_plugins[ $k ] = $formatted;
|
||||
@@ -381,10 +417,9 @@ class WC_Tracker {
|
||||
* @return array
|
||||
*/
|
||||
private static function get_order_counts() {
|
||||
$order_count = array();
|
||||
$order_count_data = wp_count_posts( 'shop_order' );
|
||||
$order_count = array();
|
||||
foreach ( wc_get_order_statuses() as $status_slug => $status_name ) {
|
||||
$order_count[ $status_slug ] = $order_count_data->{ $status_slug };
|
||||
$order_count[ $status_slug ] = wc_orders_count( $status_slug );
|
||||
}
|
||||
return $order_count;
|
||||
}
|
||||
@@ -413,33 +448,59 @@ class WC_Tracker {
|
||||
private static function get_order_totals() {
|
||||
global $wpdb;
|
||||
|
||||
$gross_total = $wpdb->get_var(
|
||||
$orders_table = OrdersTableDataStore::get_orders_table_name();
|
||||
|
||||
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$gross_total = $wpdb->get_var(
|
||||
"
|
||||
SELECT SUM(total_amount) AS 'gross_total'
|
||||
FROM $orders_table
|
||||
WHERE status in ('wc-completed', 'wc-refunded');
|
||||
"
|
||||
SELECT
|
||||
SUM( order_meta.meta_value ) AS 'gross_total'
|
||||
FROM {$wpdb->prefix}posts AS orders
|
||||
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
|
||||
WHERE order_meta.meta_key = '_order_total'
|
||||
AND orders.post_status in ( 'wc-completed', 'wc-refunded' )
|
||||
GROUP BY order_meta.meta_key
|
||||
"
|
||||
);
|
||||
);
|
||||
// phpcs:enable
|
||||
} else {
|
||||
$gross_total = $wpdb->get_var(
|
||||
"
|
||||
SELECT
|
||||
SUM( order_meta.meta_value ) AS 'gross_total'
|
||||
FROM {$wpdb->prefix}posts AS orders
|
||||
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
|
||||
WHERE order_meta.meta_key = '_order_total'
|
||||
AND orders.post_status in ( 'wc-completed', 'wc-refunded' )
|
||||
GROUP BY order_meta.meta_key
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_null( $gross_total ) ) {
|
||||
$gross_total = 0;
|
||||
}
|
||||
|
||||
$processing_gross_total = $wpdb->get_var(
|
||||
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$processing_gross_total = $wpdb->get_var(
|
||||
"
|
||||
SELECT SUM(total_amount) AS 'gross_total'
|
||||
FROM $orders_table
|
||||
WHERE status = 'wc-processing';
|
||||
"
|
||||
SELECT
|
||||
SUM( order_meta.meta_value ) AS 'gross_total'
|
||||
FROM {$wpdb->prefix}posts AS orders
|
||||
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
|
||||
WHERE order_meta.meta_key = '_order_total'
|
||||
AND orders.post_status = 'wc-processing'
|
||||
GROUP BY order_meta.meta_key
|
||||
"
|
||||
);
|
||||
);
|
||||
// phpcs:enable
|
||||
} else {
|
||||
$processing_gross_total = $wpdb->get_var(
|
||||
"
|
||||
SELECT
|
||||
SUM( order_meta.meta_value ) AS 'gross_total'
|
||||
FROM {$wpdb->prefix}posts AS orders
|
||||
LEFT JOIN {$wpdb->prefix}postmeta AS order_meta ON order_meta.post_id = orders.ID
|
||||
WHERE order_meta.meta_key = '_order_total'
|
||||
AND orders.post_status = 'wc-processing'
|
||||
GROUP BY order_meta.meta_key
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_null( $processing_gross_total ) ) {
|
||||
$processing_gross_total = 0;
|
||||
@@ -459,16 +520,31 @@ class WC_Tracker {
|
||||
private static function get_order_dates() {
|
||||
global $wpdb;
|
||||
|
||||
$min_max = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
MIN( post_date_gmt ) as 'first', MAX( post_date_gmt ) as 'last'
|
||||
FROM {$wpdb->prefix}posts
|
||||
WHERE post_type = 'shop_order'
|
||||
AND post_status = 'wc-completed'
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
$orders_table = OrdersTableDataStore::get_orders_table_name();
|
||||
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$min_max = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
MIN( date_created_gmt ) as 'first', MAX( date_created_gmt ) as 'last'
|
||||
FROM $orders_table
|
||||
WHERE status = 'wc-completed';
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
// phpcs:enable
|
||||
} else {
|
||||
$min_max = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
MIN( post_date_gmt ) as 'first', MAX( post_date_gmt ) as 'last'
|
||||
FROM {$wpdb->prefix}posts
|
||||
WHERE post_type = 'shop_order'
|
||||
AND post_status = 'wc-completed'
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_null( $min_max ) ) {
|
||||
$min_max = array(
|
||||
@@ -477,16 +553,30 @@ class WC_Tracker {
|
||||
);
|
||||
}
|
||||
|
||||
$processing_min_max = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
MIN( post_date_gmt ) as 'processing_first', MAX( post_date_gmt ) as 'processing_last'
|
||||
FROM {$wpdb->prefix}posts
|
||||
WHERE post_type = 'shop_order'
|
||||
AND post_status = 'wc-processing'
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$processing_min_max = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
MIN( date_created_gmt ) as 'processing_first', MAX( date_created_gmt ) as 'processing_last'
|
||||
FROM $orders_table
|
||||
WHERE status = 'wc-processing';
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
// phpcs:enable
|
||||
} else {
|
||||
$processing_min_max = $wpdb->get_row(
|
||||
"
|
||||
SELECT
|
||||
MIN( post_date_gmt ) as 'processing_first', MAX( post_date_gmt ) as 'processing_last'
|
||||
FROM {$wpdb->prefix}posts
|
||||
WHERE post_type = 'shop_order'
|
||||
AND post_status = 'wc-processing'
|
||||
",
|
||||
ARRAY_A
|
||||
);
|
||||
}
|
||||
|
||||
if ( is_null( $processing_min_max ) ) {
|
||||
$processing_min_max = array(
|
||||
@@ -498,6 +588,69 @@ class WC_Tracker {
|
||||
return array_merge( $min_max, $processing_min_max );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the group key for an associative array of objects which have unique ids in the key.
|
||||
* A 'group_key' property is introduced in the object.
|
||||
* For example, two objects with keys like 'WooDataPay ** #123' and 'WooDataPay ** #78' would
|
||||
* both have a group_key of 'WooDataPay **' after this function call.
|
||||
*
|
||||
* @param array $objects The array of objects that need to be grouped.
|
||||
* @param string $default_key The property that will be the default group_key.
|
||||
* @return array Contains the objects with a group_key property.
|
||||
*/
|
||||
private static function extract_group_key( $objects, $default_key ) {
|
||||
$keys = array_keys( $objects );
|
||||
|
||||
// Sort keys by length and then by characters within the same length keys.
|
||||
usort(
|
||||
$keys,
|
||||
function( $a, $b ) {
|
||||
if ( strlen( $a ) === strlen( $b ) ) {
|
||||
return strcmp( $a, $b );
|
||||
}
|
||||
return ( strlen( $a ) < strlen( $b ) ) ? -1 : 1;
|
||||
}
|
||||
);
|
||||
|
||||
// Look for common tokens in every pair of adjacent keys.
|
||||
$prev = '';
|
||||
foreach ( $keys as $key ) {
|
||||
if ( $prev ) {
|
||||
$comm_tokens = array();
|
||||
|
||||
// Tokenize the current and previous gateway names.
|
||||
$curr_tokens = preg_split( '/[ :,\-_]+/', $key );
|
||||
$prev_tokens = preg_split( '/[ :,\-_]+/', $prev );
|
||||
|
||||
$len_curr = count( $curr_tokens );
|
||||
$len_prev = count( $prev_tokens );
|
||||
|
||||
$index_unique = -1;
|
||||
// Gather the common tokens.
|
||||
// Let us allow for the unique reference id to be anywhere in the name.
|
||||
for ( $i = 0; $i < $len_curr && $i < $len_prev; $i++ ) {
|
||||
if ( $curr_tokens[ $i ] === $prev_tokens[ $i ] ) {
|
||||
$comm_tokens[] = $curr_tokens[ $i ];
|
||||
} elseif ( preg_match( '/\d/', $curr_tokens[ $i ] ) && preg_match( '/\d/', $prev_tokens[ $i ] ) ) {
|
||||
$index_unique = $i;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ) {
|
||||
$objects[ $key ]->group_key = implode( ' ', $comm_tokens );
|
||||
$objects[ $prev ]->group_key = implode( ' ', $comm_tokens );
|
||||
} else {
|
||||
$objects[ $key ]->group_key = $objects[ $key ]->$default_key;
|
||||
}
|
||||
} else {
|
||||
$objects[ $key ]->group_key = $objects[ $key ]->$default_key;
|
||||
}
|
||||
$prev = $key;
|
||||
}
|
||||
return $objects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order details by gateway.
|
||||
*
|
||||
@@ -506,38 +659,94 @@ class WC_Tracker {
|
||||
private static function get_orders_by_gateway() {
|
||||
global $wpdb;
|
||||
|
||||
$orders_by_gateway = $wpdb->get_results(
|
||||
"
|
||||
SELECT
|
||||
gateway, currency, SUM(total) AS totals, COUNT(order_id) AS counts
|
||||
FROM (
|
||||
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
$orders_table = OrdersTableDataStore::get_orders_table_name();
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$orders_and_gateway_details = $wpdb->get_results(
|
||||
"
|
||||
SELECT payment_method AS gateway, currency AS currency, SUM( total_amount ) AS totals, count( id ) AS counts
|
||||
FROM $orders_table
|
||||
WHERE status IN ( 'wc-completed', 'wc-processing', 'wc-refunded' )
|
||||
GROUP BY gateway, currency;
|
||||
"
|
||||
);
|
||||
// phpcs:enable
|
||||
} else {
|
||||
$orders_and_gateway_details = $wpdb->get_results(
|
||||
"
|
||||
SELECT
|
||||
orders.id AS order_id,
|
||||
MAX(CASE WHEN meta_key = '_payment_method' THEN meta_value END) gateway,
|
||||
MAX(CASE WHEN meta_key = '_order_total' THEN meta_value END) total,
|
||||
MAX(CASE WHEN meta_key = '_order_currency' THEN meta_value END) currency
|
||||
FROM
|
||||
{$wpdb->prefix}posts orders
|
||||
LEFT JOIN
|
||||
{$wpdb->prefix}postmeta order_meta ON order_meta.post_id = orders.id
|
||||
WHERE orders.post_type = 'shop_order'
|
||||
AND orders.post_status in ( 'wc-completed', 'wc-processing', 'wc-refunded' )
|
||||
AND meta_key in( '_payment_method','_order_total','_order_currency')
|
||||
GROUP BY orders.id
|
||||
) order_gateways
|
||||
GROUP BY gateway, currency
|
||||
"
|
||||
);
|
||||
gateway, currency, SUM(total) AS totals, COUNT(order_id) AS counts
|
||||
FROM (
|
||||
SELECT
|
||||
orders.id AS order_id,
|
||||
MAX(CASE WHEN meta_key = '_payment_method' THEN meta_value END) gateway,
|
||||
MAX(CASE WHEN meta_key = '_order_total' THEN meta_value END) total,
|
||||
MAX(CASE WHEN meta_key = '_order_currency' THEN meta_value END) currency
|
||||
FROM
|
||||
{$wpdb->prefix}posts orders
|
||||
LEFT JOIN
|
||||
{$wpdb->prefix}postmeta order_meta ON order_meta.post_id = orders.id
|
||||
WHERE orders.post_type = 'shop_order'
|
||||
AND orders.post_status in ( 'wc-completed', 'wc-processing', 'wc-refunded' )
|
||||
AND meta_key in( '_payment_method','_order_total','_order_currency')
|
||||
GROUP BY orders.id
|
||||
) order_gateways
|
||||
GROUP BY gateway, currency
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
$orders_by_gateway_currency = array();
|
||||
foreach ( $orders_by_gateway as $orders_details ) {
|
||||
$gateway = 'gateway_' . $orders_details->gateway;
|
||||
$currency = $orders_details->currency;
|
||||
$count = $gateway . '_' . $currency . '_count';
|
||||
$total = $gateway . '_' . $currency . '_total';
|
||||
|
||||
$orders_by_gateway_currency[ $count ] = $orders_details->counts;
|
||||
$orders_by_gateway_currency[ $total ] = $orders_details->totals;
|
||||
// The associative array that is created as the result of array_reduce is passed to extract_group_key()
|
||||
// This function has the logic that will remove specific transaction identifiers that may sometimes be part of a
|
||||
// payment method. For example, two payments methods like 'WooDataPay ** #123' and 'WooDataPay ** #78' would
|
||||
// both have the same group_key 'WooDataPay **'.
|
||||
$orders_by_gateway = self::extract_group_key(
|
||||
// Convert into an associative array with a combination of currency and gateway as key.
|
||||
array_reduce(
|
||||
$orders_and_gateway_details,
|
||||
function( $result, $item ) {
|
||||
$item->gateway = preg_replace( '/\s+/', ' ', $item->gateway );
|
||||
|
||||
// Introduce currency as a prefix for the key.
|
||||
$key = $item->currency . '==' . $item->gateway;
|
||||
|
||||
$result[ $key ] = $item;
|
||||
return $result;
|
||||
},
|
||||
array()
|
||||
),
|
||||
'gateway'
|
||||
);
|
||||
|
||||
// Aggregate using group_key.
|
||||
foreach ( $orders_by_gateway as $orders_details ) {
|
||||
$gkey = $orders_details->group_key;
|
||||
|
||||
// Remove currency as prefix of key for backward compatibility.
|
||||
if ( str_contains( $gkey, '==' ) ) {
|
||||
$tokens = preg_split( '/==/', $gkey );
|
||||
$key = $tokens[1];
|
||||
} else {
|
||||
$key = $gkey;
|
||||
}
|
||||
|
||||
$key = str_replace( array( 'payment method', 'payment gateway', 'gateway' ), '', strtolower( $key ) );
|
||||
$key = trim( preg_replace( '/[: ,#*\-_]+/', ' ', $key ) );
|
||||
|
||||
// Add currency as postfix of gateway for backward compatibility.
|
||||
$key = 'gateway_' . $key . '_' . $orders_details->currency;
|
||||
$count_key = $key . '_count';
|
||||
$total_key = $key . '_total';
|
||||
|
||||
if ( array_key_exists( $count_key, $orders_by_gateway_currency ) || array_key_exists( $total_key, $orders_by_gateway_currency ) ) {
|
||||
$orders_by_gateway_currency[ $count_key ] = $orders_by_gateway_currency[ $count_key ] + $orders_details->counts;
|
||||
$orders_by_gateway_currency[ $total_key ] = $orders_by_gateway_currency[ $total_key ] + $orders_details->totals;
|
||||
} else {
|
||||
$orders_by_gateway_currency[ $count_key ] = $orders_details->counts;
|
||||
$orders_by_gateway_currency[ $total_key ] = $orders_details->totals;
|
||||
}
|
||||
}
|
||||
|
||||
return $orders_by_gateway_currency;
|
||||
@@ -551,24 +760,63 @@ class WC_Tracker {
|
||||
private static function get_orders_origins() {
|
||||
global $wpdb;
|
||||
|
||||
$orders_origin = $wpdb->get_results(
|
||||
"
|
||||
SELECT
|
||||
meta_value as origin, COUNT( DISTINCT ( orders.id ) ) as count
|
||||
FROM
|
||||
$wpdb->posts orders
|
||||
LEFT JOIN
|
||||
$wpdb->postmeta order_meta ON order_meta.post_id = orders.id
|
||||
WHERE
|
||||
meta_key = '_created_via'
|
||||
GROUP BY
|
||||
meta_value;
|
||||
if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
|
||||
$op_table_name = OrdersTableDataStore::get_operational_data_table_name();
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$orders_origin = $wpdb->get_results(
|
||||
"
|
||||
SELECT created_via as origin, COUNT( order_id ) as count
|
||||
FROM $op_table_name
|
||||
GROUP BY created_via;
|
||||
"
|
||||
);
|
||||
// phpcs:enable
|
||||
} else {
|
||||
$orders_origin = $wpdb->get_results(
|
||||
"
|
||||
SELECT
|
||||
meta_value as origin, COUNT( DISTINCT ( orders.id ) ) as count
|
||||
FROM
|
||||
$wpdb->posts orders
|
||||
LEFT JOIN
|
||||
$wpdb->postmeta order_meta ON order_meta.post_id = orders.id
|
||||
WHERE
|
||||
meta_key = '_created_via'
|
||||
GROUP BY
|
||||
meta_value;
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
// The associative array that is created as the result of array_reduce is passed to extract_group_key()
|
||||
// This function has the logic that will remove specific identifiers that may sometimes be part of an origin.
|
||||
// For example, two origins like 'Import #123' and 'Import ** #78' would both have a group_key 'Import **'.
|
||||
$orders_and_origins = self::extract_group_key(
|
||||
// Convert into an associative array with the origin as key.
|
||||
array_reduce(
|
||||
$orders_origin,
|
||||
function( $result, $item ) {
|
||||
$key = $item->origin;
|
||||
|
||||
$result[ $key ] = $item;
|
||||
return $result;
|
||||
},
|
||||
array()
|
||||
),
|
||||
'origin'
|
||||
);
|
||||
|
||||
$orders_by_origin = array();
|
||||
foreach ( $orders_origin as $origin ) {
|
||||
$orders_by_origin[ $origin->origin ] = (int) $origin->count;
|
||||
|
||||
// Aggregate using group_key.
|
||||
foreach ( $orders_and_origins as $origin ) {
|
||||
$key = strtolower( $origin->group_key );
|
||||
|
||||
if ( array_key_exists( $key, $orders_by_origin ) ) {
|
||||
$orders_by_origin[ $key ] = $orders_by_origin[ $key ] + (int) $origin->count;
|
||||
} else {
|
||||
$orders_by_origin[ $key ] = (int) $origin->count;
|
||||
}
|
||||
}
|
||||
|
||||
return array( 'created_via' => $orders_by_origin );
|
||||
@@ -663,6 +911,23 @@ class WC_Tracker {
|
||||
return $active_methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of slugs for WC features that are enabled on the site.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private static function get_enabled_features() {
|
||||
$all_features = FeaturesUtil::get_features( true, true );
|
||||
$enabled_features = array_filter(
|
||||
$all_features,
|
||||
function( $feature ) {
|
||||
return $feature['is_enabled'];
|
||||
}
|
||||
);
|
||||
|
||||
return array_keys( $enabled_features );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all options starting with woocommerce_ prefix.
|
||||
*
|
||||
@@ -705,7 +970,12 @@ class WC_Tracker {
|
||||
* @return array
|
||||
*/
|
||||
private static function get_all_template_overrides() {
|
||||
$override_data = array();
|
||||
$override_data = array();
|
||||
/**
|
||||
* Filter the paths to scan for template overrides.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*/
|
||||
$template_paths = apply_filters( 'woocommerce_template_overrides_scan_paths', array( 'WooCommerce' => WC()->plugin_path() . '/templates/' ) );
|
||||
$scanned_files = array();
|
||||
|
||||
|
||||
@@ -997,9 +997,11 @@ class WC_Webhook extends WC_Legacy_Webhook {
|
||||
),
|
||||
'order.deleted' => array(
|
||||
'wp_trash_post',
|
||||
'woocommerce_trash_order',
|
||||
),
|
||||
'order.restored' => array(
|
||||
'untrashed_post',
|
||||
'woocommerce_untrash_order',
|
||||
),
|
||||
'product.created' => array(
|
||||
'woocommerce_process_product_meta',
|
||||
|
||||
@@ -18,6 +18,7 @@ use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
|
||||
use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as ProductDownloadDirectories;
|
||||
use Automattic\WooCommerce\Internal\RestockRefundedItemsAdjuster;
|
||||
use Automattic\WooCommerce\Internal\Settings\OptionSanitizer;
|
||||
use Automattic\WooCommerce\Internal\Utilities\WebhookUtil;
|
||||
use Automattic\WooCommerce\Proxies\LegacyProxy;
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,7 @@ final class WooCommerce {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = '7.7.2';
|
||||
public $version = '8.1.1';
|
||||
|
||||
/**
|
||||
* WooCommerce Schema version.
|
||||
@@ -203,6 +204,22 @@ final class WooCommerce {
|
||||
do_action( 'woocommerce_loaded' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiali Jetpack Connection Config.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_jetpack_connection_config() {
|
||||
$config = new Automattic\Jetpack\Config();
|
||||
$config->ensure(
|
||||
'connection',
|
||||
array(
|
||||
'slug' => 'woocommerce',
|
||||
'name' => __( 'WooCommerce', 'woocommerce' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into actions and filters.
|
||||
*
|
||||
@@ -213,6 +230,7 @@ final class WooCommerce {
|
||||
register_shutdown_function( array( $this, 'log_errors' ) );
|
||||
|
||||
add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ), -1 );
|
||||
add_action( 'plugins_loaded', array( $this, 'init_jetpack_connection_config' ), 1 );
|
||||
add_action( 'admin_notices', array( $this, 'build_dependencies_notice' ) );
|
||||
add_action( 'after_setup_theme', array( $this, 'setup_environment' ) );
|
||||
add_action( 'after_setup_theme', array( $this, 'include_template_functions' ), 11 );
|
||||
@@ -221,6 +239,7 @@ final class WooCommerce {
|
||||
add_action( 'init', array( 'WC_Emails', 'init_transactional_emails' ) );
|
||||
add_action( 'init', array( $this, 'add_image_sizes' ) );
|
||||
add_action( 'init', array( $this, 'load_rest_api' ) );
|
||||
add_action( 'init', array( 'WC_Site_Tracking', 'init' ) );
|
||||
add_action( 'switch_blog', array( $this, 'wpdb_table_fix' ), 0 );
|
||||
add_action( 'activated_plugin', array( $this, 'activated_plugin' ) );
|
||||
add_action( 'deactivated_plugin', array( $this, 'deactivated_plugin' ) );
|
||||
@@ -239,6 +258,7 @@ final class WooCommerce {
|
||||
$container->get( OptionSanitizer::class );
|
||||
$container->get( BatchProcessingController::class );
|
||||
$container->get( FeaturesController::class );
|
||||
$container->get( WebhookUtil::class );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -532,6 +552,15 @@ final class WooCommerce {
|
||||
include_once WC_ABSPATH . 'includes/class-wc-auth.php';
|
||||
include_once WC_ABSPATH . 'includes/class-wc-register-wp-admin-settings.php';
|
||||
|
||||
/**
|
||||
* Tracks.
|
||||
*/
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks.php';
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-event.php';
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-client.php';
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-footer-pixel.php';
|
||||
include_once WC_ABSPATH . 'includes/tracks/class-wc-site-tracking.php';
|
||||
|
||||
/**
|
||||
* WCCOM Site.
|
||||
*/
|
||||
|
||||
+129
-11
@@ -106,6 +106,23 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an order exists by id.
|
||||
*
|
||||
* @since 8.0.0
|
||||
*
|
||||
* @param int $order_id The order id to check.
|
||||
* @return bool True if an order exists with the given name.
|
||||
*/
|
||||
public function order_exists( $order_id ) : bool {
|
||||
if ( ! $order_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$post_object = get_post( $order_id );
|
||||
return ! is_null( $post_object ) && in_array( $post_object->post_type, wc_get_order_types(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to read an order from the database.
|
||||
*
|
||||
@@ -120,7 +137,8 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
throw new Exception( __( 'Invalid order.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$order->set_props(
|
||||
$this->set_order_props(
|
||||
$order,
|
||||
array(
|
||||
'parent_id' => $post_object->post_parent,
|
||||
'date_created' => $this->string_to_timestamp( $post_object->post_date_gmt ),
|
||||
@@ -143,6 +161,43 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the properties of an object and log the first error found while doing so.
|
||||
*
|
||||
* @param $order WC_Order $order Order object.
|
||||
* @param array $props The properties to set.
|
||||
*/
|
||||
private function set_order_props( &$order, array $props ) {
|
||||
$errors = $order->set_props( $props );
|
||||
|
||||
if ( ! $errors instanceof WP_Error ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order_id = $order->get_id();
|
||||
$logger = WC()->call_function( 'wc_get_logger' );
|
||||
|
||||
foreach ( $errors->get_error_codes() as $error_code ) {
|
||||
$property_name = $errors->get_error_data( $error_code )['property_name'] ?? '';
|
||||
$error_message = $errors->get_error_message( $error_code );
|
||||
$logger->warning(
|
||||
sprintf(
|
||||
/* translators: %1$s = order ID, %2$s = order id, %3$s = error message. */
|
||||
__( 'Error when setting property \'%1$s\' for order %2$d: %3$s', 'woocommerce' ),
|
||||
$property_name,
|
||||
$order_id,
|
||||
$error_message
|
||||
),
|
||||
array(
|
||||
'error_code' => $error_code,
|
||||
'error_message' => $error_message,
|
||||
'order_id' => $order_id,
|
||||
'property_name' => $property_name,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to update an order in the database.
|
||||
*
|
||||
@@ -204,7 +259,8 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
$args = wp_parse_args(
|
||||
$args,
|
||||
array(
|
||||
'force_delete' => false,
|
||||
'force_delete' => false,
|
||||
'suppress_filters' => false,
|
||||
)
|
||||
);
|
||||
|
||||
@@ -212,14 +268,60 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
return;
|
||||
}
|
||||
|
||||
$do_filters = ! $args['suppress_filters'];
|
||||
|
||||
if ( $args['force_delete'] ) {
|
||||
if ( $do_filters ) {
|
||||
/**
|
||||
* Fires immediately before an order is deleted from the database.
|
||||
*
|
||||
* @since 8.0.0
|
||||
*
|
||||
* @param int $order_id ID of the order about to be deleted.
|
||||
* @param WC_Order $order Instance of the order that is about to be deleted.
|
||||
*/
|
||||
do_action( 'woocommerce_before_delete_order', $id, $order );
|
||||
}
|
||||
|
||||
wp_delete_post( $id );
|
||||
$order->set_id( 0 );
|
||||
do_action( 'woocommerce_delete_order', $id );
|
||||
|
||||
if ( $do_filters ) {
|
||||
/**
|
||||
* Fires immediately after an order is deleted.
|
||||
*
|
||||
* @since
|
||||
*
|
||||
* @param int $order_id ID of the order that has been deleted.
|
||||
*/
|
||||
do_action( 'woocommerce_delete_order', $id );
|
||||
}
|
||||
} else {
|
||||
if ( $do_filters ) {
|
||||
/**
|
||||
* Fires immediately before an order is trashed.
|
||||
*
|
||||
* @since 8.0.0
|
||||
*
|
||||
* @param int $order_id ID of the order about to be trashed.
|
||||
* @param WC_Order $order Instance of the order that is about to be trashed.
|
||||
*/
|
||||
do_action( 'woocommerce_before_trash_order', $id, $order );
|
||||
}
|
||||
|
||||
wp_trash_post( $id );
|
||||
$order->set_status( 'trash' );
|
||||
do_action( 'woocommerce_trash_order', $id );
|
||||
|
||||
if ( $do_filters ) {
|
||||
/**
|
||||
* Fires immediately after an order is trashed.
|
||||
*
|
||||
* @since
|
||||
*
|
||||
* @param int $order_id ID of the order that has been trashed.
|
||||
*/
|
||||
do_action( 'woocommerce_trash_order', $id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,7 +404,8 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
protected function read_order_data( &$order, $post_object ) {
|
||||
$id = $order->get_id();
|
||||
|
||||
$order->set_props(
|
||||
$this->set_order_props(
|
||||
$order,
|
||||
array(
|
||||
'currency' => get_post_meta( $id, '_order_currency', true ),
|
||||
'discount_total' => get_post_meta( $id, '_cart_discount', true ),
|
||||
@@ -599,11 +702,11 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to update order metadata from intialized order object.
|
||||
* Helper method to update order metadata from initialized order object.
|
||||
*
|
||||
* @param WC_Abstract_Order $order Order object.
|
||||
*/
|
||||
private function update_order_meta_from_object( $order ) {
|
||||
protected function update_order_meta_from_object( $order ) {
|
||||
if ( is_null( $order->get_meta() ) ) {
|
||||
return;
|
||||
}
|
||||
@@ -612,13 +715,24 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
|
||||
foreach ( $order->get_meta_data() as $meta_data ) {
|
||||
if ( isset( $existing_meta_data[ $meta_data->key ] ) ) {
|
||||
if ( $existing_meta_data[ $meta_data->key ] === $meta_data->value ) {
|
||||
// We don't know if the meta is single or array, so we assume it to be an array.
|
||||
$meta_value = is_array( $meta_data->value ) ? $meta_data->value : array( $meta_data->value );
|
||||
|
||||
if ( $existing_meta_data[ $meta_data->key ] === $meta_value ) {
|
||||
unset( $existing_meta_data[ $meta_data->key ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
unset( $existing_meta_data[ $meta_data->key ] );
|
||||
delete_post_meta( $order->get_id(), $meta_data->key );
|
||||
if ( is_array( $existing_meta_data[ $meta_data->key ] ) ) {
|
||||
$value_index = array_search( $meta_data->value, $existing_meta_data[ $meta_data->key ], true );
|
||||
if ( false !== $value_index ) {
|
||||
unset( $existing_meta_data[ $meta_data->key ][ $value_index ] );
|
||||
if ( 0 === count( $existing_meta_data[ $meta_data->key ] ) ) {
|
||||
unset( $existing_meta_data[ $meta_data->key ] );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
add_post_meta( $order->get_id(), $meta_data->key, $meta_data->value, false );
|
||||
}
|
||||
@@ -632,7 +746,11 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
||||
);
|
||||
|
||||
foreach ( $keys_to_delete as $meta_key ) {
|
||||
delete_post_meta( $order->get_id(), $meta_key );
|
||||
if ( isset( $existing_meta_data[ $meta_key ] ) ) {
|
||||
foreach ( $existing_meta_data[ $meta_key ] as $meta_value ) {
|
||||
delete_post_meta( $order->get_id(), $meta_key, maybe_unserialize( $meta_value ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->update_post_meta( $order );
|
||||
|
||||
+6
-6
@@ -532,16 +532,16 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
);
|
||||
|
||||
$query_for_tentative_usages = $this->get_tentative_usage_query( $coupon->get_id() );
|
||||
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM DUAL' );
|
||||
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM ' . $wpdb->posts . ' LIMIT 1' );
|
||||
|
||||
$coupon_usage_key = '_coupon_held_' . ( (int) $db_timestamp + $held_time ) . '_' . wp_generate_password( 6, false );
|
||||
|
||||
$insert_statement = $wpdb->prepare(
|
||||
"
|
||||
INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value )
|
||||
SELECT %d, %s, %s FROM DUAL
|
||||
SELECT %d, %s, %s FROM $wpdb->posts
|
||||
WHERE ( $query_for_usages ) + ( $query_for_tentative_usages ) < %d
|
||||
",
|
||||
LIMIT 1",
|
||||
$coupon->get_id(),
|
||||
$coupon_usage_key,
|
||||
'',
|
||||
@@ -629,15 +629,15 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
$query_for_tentative_usages = $this->get_tentative_usage_query_for_user( $coupon->get_id(), $user_aliases );
|
||||
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM DUAL' );
|
||||
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM ' . $wpdb->posts . ' LIMIT 1' );
|
||||
|
||||
$coupon_used_by_meta_key = '_maybe_used_by_' . ( (int) $db_timestamp + $held_time ) . '_' . wp_generate_password( 6, false );
|
||||
$insert_statement = $wpdb->prepare(
|
||||
"
|
||||
INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value )
|
||||
SELECT %d, %s, %s FROM DUAL
|
||||
SELECT %d, %s, %s FROM $wpdb->posts
|
||||
WHERE ( $query_for_usages ) + ( $query_for_tentative_usages ) < %d
|
||||
",
|
||||
LIMIT 1",
|
||||
$coupon->get_id(),
|
||||
$coupon_used_by_meta_key,
|
||||
$user_alias,
|
||||
|
||||
+28
-1
@@ -538,7 +538,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
||||
|
||||
$unpaid_orders = $wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
// @codingStandardsIgnoreStart
|
||||
// @codingStandardsIgnoreStart
|
||||
"SELECT posts.ID
|
||||
FROM {$wpdb->posts} AS posts
|
||||
WHERE posts.post_type IN ('" . implode( "','", wc_get_order_types() ) . "')
|
||||
@@ -577,6 +577,7 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
||||
'_shipping_address_index',
|
||||
'_billing_last_name',
|
||||
'_billing_email',
|
||||
'_billing_phone',
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -603,6 +604,16 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
||||
WHERE order_item_name LIKE %s",
|
||||
'%' . $wpdb->esc_like( wc_clean( $term ) ) . '%'
|
||||
)
|
||||
),
|
||||
$wpdb->get_col(
|
||||
$wpdb->prepare(
|
||||
"SELECT DISTINCT os.order_id FROM {$wpdb->prefix}wc_order_stats os
|
||||
INNER JOIN {$wpdb->prefix}wc_customer_lookup cl ON os.customer_id = cl.customer_id
|
||||
INNER JOIN {$wpdb->usermeta} um ON cl.user_id = um.user_id
|
||||
WHERE (um.meta_key = 'billing_phone' OR um.meta_key = 'shipping_phone')
|
||||
AND um.meta_value = %s",
|
||||
wc_clean( $term )
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -1170,4 +1181,20 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
||||
);
|
||||
WC_Order::prime_raw_meta_data_cache( $raw_meta_data_collection, 'orders' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to restore the specified order back to its original status (after having been trashed).
|
||||
*
|
||||
* @param WC_Order $order The order to be untrashed.
|
||||
*
|
||||
* @return bool If the operation was successful.
|
||||
*/
|
||||
public function untrash_order( WC_Order $order ): bool {
|
||||
if ( ! wp_untrash_post( $order->get_id() ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$order->set_status( get_post_field( 'post_status', $order->get_id() ) );
|
||||
return (bool) $order->save();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -203,7 +203,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
||||
$changes = $product->get_changes();
|
||||
|
||||
// Only update the post when the post data changes.
|
||||
if ( array_intersect( array( 'description', 'short_description', 'name', 'parent_id', 'reviews_allowed', 'status', 'menu_order', 'date_created', 'date_modified', 'slug' ), array_keys( $changes ) ) ) {
|
||||
if ( array_intersect( array( 'description', 'short_description', 'name', 'parent_id', 'reviews_allowed', 'status', 'menu_order', 'date_created', 'date_modified', 'slug', 'post_password' ), array_keys( $changes ) ) ) {
|
||||
$post_data = array(
|
||||
'post_content' => $product->get_description( 'edit' ),
|
||||
'post_excerpt' => $product->get_short_description( 'edit' ),
|
||||
@@ -1137,7 +1137,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
||||
/**
|
||||
* Check each variation to find the one that matches the $match_attributes.
|
||||
*
|
||||
* Note: Not all meta fields will be set which is why we check existance.
|
||||
* Note: Not all meta fields will be set which is why we check existence.
|
||||
*/
|
||||
foreach ( $sorted_meta as $variation_id => $variation ) {
|
||||
$match = true;
|
||||
|
||||
+2
-2
@@ -119,11 +119,11 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
|
||||
public function read_children( &$product, $force_read = false ) {
|
||||
$children_transient_name = 'wc_product_children_' . $product->get_id();
|
||||
$children = get_transient( $children_transient_name );
|
||||
if ( false === $children ) {
|
||||
if ( empty( $children ) || ! is_array( $children ) ) {
|
||||
$children = array();
|
||||
}
|
||||
|
||||
if ( empty( $children ) || ! is_array( $children ) || ! isset( $children['all'] ) || ! isset( $children['visible'] ) || $force_read ) {
|
||||
if ( ! isset( $children['all'] ) || ! isset( $children['visible'] ) || $force_read ) {
|
||||
$all_args = array(
|
||||
'post_parent' => $product->get_id(),
|
||||
'post_type' => 'product_variation',
|
||||
|
||||
@@ -282,6 +282,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
|
||||
$exclude = '';
|
||||
$date_created = '';
|
||||
$date_modified = '';
|
||||
$user_id = '';
|
||||
|
||||
if ( ! empty( $args['include'] ) ) {
|
||||
$args['include'] = implode( ',', wp_parse_id_list( $args['include'] ) );
|
||||
@@ -293,6 +294,10 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
|
||||
$exclude = 'AND webhook_id NOT IN (' . $args['exclude'] . ')';
|
||||
}
|
||||
|
||||
if ( ! empty( $args['user_id'] ) ) {
|
||||
$user_id = $wpdb->prepare( 'AND `user_id` = %d', absint( $args['user_id'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $args['after'] ) || ! empty( $args['before'] ) ) {
|
||||
$args['after'] = empty( $args['after'] ) ? '0000-00-00' : $args['after'];
|
||||
$args['before'] = empty( $args['before'] ) ? current_time( 'mysql', 1 ) : $args['before'];
|
||||
@@ -326,6 +331,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
|
||||
{$exclude}
|
||||
{$date_created}
|
||||
{$date_modified}
|
||||
{$user_id}
|
||||
{$order}
|
||||
{$limit}
|
||||
{$offset}"
|
||||
@@ -349,6 +355,7 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
|
||||
{$exclude}
|
||||
{$date_created}
|
||||
{$date_modified}
|
||||
{$user_id}
|
||||
{$order}
|
||||
{$limit}
|
||||
{$offset}"
|
||||
|
||||
@@ -165,6 +165,23 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
||||
return __( 'Congratulations on the sale.', 'woocommerce' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return content from the additional_content field.
|
||||
*
|
||||
* Displayed above the footer.
|
||||
*
|
||||
* @since 3.7.0
|
||||
* @return string
|
||||
*/
|
||||
public function get_additional_content() {
|
||||
/**
|
||||
* This filter is documented in ./class-wc-email.php
|
||||
*
|
||||
* @since 7.8.0
|
||||
*/
|
||||
return apply_filters( 'woocommerce_email_additional_content_' . $this->id, $this->format_string( $this->get_option( 'additional_content' ) ), $this->object, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise settings form fields.
|
||||
*/
|
||||
|
||||
@@ -228,6 +228,13 @@ class WC_Email extends WC_Settings_API {
|
||||
*/
|
||||
public $replace = array();
|
||||
|
||||
/**
|
||||
* E-mail type: plain, html or multipart.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $email_type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@@ -400,6 +407,15 @@ class WC_Email extends WC_Settings_API {
|
||||
* @return string
|
||||
*/
|
||||
public function get_additional_content() {
|
||||
/**
|
||||
* Provides an opportunity to inspect and modify additional content for the email.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param string $additional_content Additional content to be added to the email.
|
||||
* @param object|bool $object The object (ie, product or order) this email relates to, if any.
|
||||
* @param WC_Email $email WC_Email instance managing the email.
|
||||
*/
|
||||
return apply_filters( 'woocommerce_email_additional_content_' . $this->id, $this->format_string( $this->get_option( 'additional_content', $this->get_default_additional_content() ) ), $this->object, $this );
|
||||
}
|
||||
|
||||
|
||||
@@ -398,8 +398,10 @@ abstract class WC_CSV_Exporter {
|
||||
$use_mb = function_exists( 'mb_convert_encoding' );
|
||||
|
||||
if ( $use_mb ) {
|
||||
$encoding = mb_detect_encoding( $data, 'UTF-8, ISO-8859-1', true );
|
||||
$data = 'UTF-8' === $encoding ? $data : utf8_encode( $data );
|
||||
$is_valid_utf_8 = mb_check_encoding( $data, 'UTF-8' );
|
||||
if ( ! $is_valid_utf_8 ) {
|
||||
$data = mb_convert_encoding( $data, 'UTF-8', 'ISO-8859-1' );
|
||||
}
|
||||
}
|
||||
|
||||
return $this->escape_data( $data );
|
||||
|
||||
@@ -182,7 +182,7 @@ class WC_Product_CSV_Exporter extends WC_CSV_Batch_Exporter {
|
||||
$variable_products = array();
|
||||
|
||||
foreach ( $products->products as $product ) {
|
||||
// Check if the category is set, this means we need to fetch variations seperately as they are not tied to a category.
|
||||
// Check if the category is set, this means we need to fetch variations separately as they are not tied to a category.
|
||||
if ( ! empty( $args['category'] ) && $product->is_type( 'variable' ) ) {
|
||||
$variable_products[] = $product->get_id();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,20 @@ class WC_Gateway_BACS extends WC_Payment_Gateway {
|
||||
*/
|
||||
public $locale;
|
||||
|
||||
/**
|
||||
* Gateway instructions that will be added to the thank you page and emails.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $instructions;
|
||||
|
||||
/**
|
||||
* Account details.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $account_details;
|
||||
|
||||
/**
|
||||
* Constructor for the gateway.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,13 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
*/
|
||||
class WC_Gateway_Cheque extends WC_Payment_Gateway {
|
||||
|
||||
/**
|
||||
* Gateway instructions that will be added to the thank you page and emails.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $instructions;
|
||||
|
||||
/**
|
||||
* Constructor for the gateway.
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,27 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
*/
|
||||
class WC_Gateway_COD extends WC_Payment_Gateway {
|
||||
|
||||
/**
|
||||
* Gateway instructions that will be added to the thank you page and emails.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $instructions;
|
||||
|
||||
/**
|
||||
* Enable for shipping methods.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $enable_for_methods;
|
||||
|
||||
/**
|
||||
* Enable for virtual products.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $enable_for_virtual;
|
||||
|
||||
/**
|
||||
* Constructor for the gateway.
|
||||
*/
|
||||
|
||||
+36
@@ -35,6 +35,42 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
|
||||
*/
|
||||
public static $log = false;
|
||||
|
||||
/**
|
||||
* Whether the test mode is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $testmode;
|
||||
|
||||
/**
|
||||
* Whether the debug mode is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $debug;
|
||||
|
||||
/**
|
||||
* Email address to send payments to.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* Receiver email.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $receiver_email;
|
||||
|
||||
/**
|
||||
* Identity token.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $identity_token;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor for the gateway.
|
||||
*/
|
||||
|
||||
@@ -250,11 +250,12 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
||||
if ( 'external' === $object->get_type() ) {
|
||||
unset( $data['manage_stock'], $data['stock_status'], $data['backorders'], $data['low_stock_amount'] );
|
||||
}
|
||||
|
||||
$is_variation = false;
|
||||
if ( 'variation' === $object->get_type() ) {
|
||||
if ( isset( $data['status'] ) && -1 === $data['status'] ) {
|
||||
$data['status'] = 0; // Variations cannot be drafts - set to private.
|
||||
}
|
||||
$is_variation = true;
|
||||
}
|
||||
|
||||
if ( 'importing' === $object->get_status() ) {
|
||||
@@ -283,8 +284,9 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
|
||||
do_action( 'woocommerce_product_import_inserted_product_object', $object, $data );
|
||||
|
||||
return array(
|
||||
'id' => $object->get_id(),
|
||||
'updated' => $updating,
|
||||
'id' => $object->get_id(),
|
||||
'updated' => $updating,
|
||||
'is_variation' => $is_variation,
|
||||
);
|
||||
} catch ( Exception $e ) {
|
||||
return new WP_Error( 'woocommerce_product_importer_error', $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||
|
||||
+41
-7
@@ -590,6 +590,35 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse dates from a CSV.
|
||||
* Dates can be Unix timestamps or in any format supported by strtotime().
|
||||
*
|
||||
* @param string $value Field value.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function parse_datetime_field( $value ) {
|
||||
try {
|
||||
// If value is a Unix timestamp, convert it to a datetime string.
|
||||
if ( is_numeric( $value ) ) {
|
||||
$datetime = new DateTime( "@{$value}" );
|
||||
// Return datetime string in ISO8601 format (eg. 2018-01-01T00:00:00Z) to preserve UTC timezone since Unix timestamps are always UTC.
|
||||
return $datetime->format( 'Y-m-d\TH:i:s\Z' );
|
||||
}
|
||||
// Check whether the value is a valid date string.
|
||||
if ( false !== strtotime( $value ) ) {
|
||||
// If the value is a valid date string, return as is.
|
||||
return $value;
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
// DateTime constructor throws an exception if the value is not a valid Unix timestamp.
|
||||
return null;
|
||||
}
|
||||
// If value is not valid Unix timestamp or date string, return null.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse backorders from a CSV.
|
||||
*
|
||||
@@ -725,8 +754,8 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
||||
'type' => array( $this, 'parse_comma_field' ),
|
||||
'published' => array( $this, 'parse_published_field' ),
|
||||
'featured' => array( $this, 'parse_bool_field' ),
|
||||
'date_on_sale_from' => array( $this, 'parse_date_field' ),
|
||||
'date_on_sale_to' => array( $this, 'parse_date_field' ),
|
||||
'date_on_sale_from' => array( $this, 'parse_datetime_field' ),
|
||||
'date_on_sale_to' => array( $this, 'parse_datetime_field' ),
|
||||
'name' => array( $this, 'parse_skip_field' ),
|
||||
'short_description' => array( $this, 'parse_description_field' ),
|
||||
'description' => array( $this, 'parse_description_field' ),
|
||||
@@ -1080,10 +1109,11 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
||||
$index = 0;
|
||||
$update_existing = $this->params['update_existing'];
|
||||
$data = array(
|
||||
'imported' => array(),
|
||||
'failed' => array(),
|
||||
'updated' => array(),
|
||||
'skipped' => array(),
|
||||
'imported' => array(),
|
||||
'imported_variations' => array(),
|
||||
'failed' => array(),
|
||||
'updated' => array(),
|
||||
'skipped' => array(),
|
||||
);
|
||||
|
||||
foreach ( $this->parsed_data as $parsed_data_key => $parsed_data ) {
|
||||
@@ -1150,7 +1180,11 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
|
||||
} elseif ( $result['updated'] ) {
|
||||
$data['updated'][] = $result['id'];
|
||||
} else {
|
||||
$data['imported'][] = $result['id'];
|
||||
if ( $result['is_variation'] ) {
|
||||
$data['imported_variations'][] = $result['id'];
|
||||
} else {
|
||||
$data['imported'][] = $result['id'];
|
||||
}
|
||||
}
|
||||
|
||||
$index ++;
|
||||
|
||||
@@ -18,6 +18,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
/**
|
||||
* Legacy cart class.
|
||||
*/
|
||||
#[AllowDynamicProperties]
|
||||
abstract class WC_Legacy_Cart {
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,8 +6,10 @@ if ( ! function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
return array(
|
||||
'activity-panels' => true,
|
||||
'analytics' => true,
|
||||
'product-block-editor' => false,
|
||||
'product-block-editor' => true,
|
||||
'coupons' => true,
|
||||
'core-profiler' => true,
|
||||
'customize-store' => false,
|
||||
'customer-effort-score-tracks' => true,
|
||||
'import-products-task' => true,
|
||||
'experimental-fashion-sample-products' => true,
|
||||
@@ -33,6 +35,7 @@ if ( ! function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
'woo-mobile-welcome' => true,
|
||||
'wc-pay-promotion' => true,
|
||||
'wc-pay-welcome-page' => true,
|
||||
'async-product-editor-category-field' => false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+38
-7
@@ -81,7 +81,22 @@ class WC_REST_Telemetry_Controller extends WC_REST_Controller {
|
||||
}
|
||||
|
||||
$platform = $new['platform'];
|
||||
if ( ! $data[ $platform ] || version_compare( $new['version'], $data[ $platform ]['version'], '>=' ) ) {
|
||||
|
||||
if ( isset( $data[ $platform ] ) ) {
|
||||
$existing_usage = $data[ $platform ];
|
||||
|
||||
// Sets the installation date only if it has not been set before.
|
||||
if ( isset( $new['installation_date'] ) && ! isset( $existing_usage['installation_date'] ) ) {
|
||||
$data[ $platform ]['installation_date'] = $new['installation_date'];
|
||||
}
|
||||
|
||||
if ( version_compare( $new['version'], $existing_usage['version'], '>=' ) ) {
|
||||
$data[ $platform ]['version'] = $new['version'];
|
||||
$data[ $platform ]['last_used'] = $new['last_used'];
|
||||
}
|
||||
} else {
|
||||
// Only sets `first_used` when the platform usage data hasn't been set before.
|
||||
$new['first_used'] = $new['last_used'];
|
||||
$data[ $platform ] = $new;
|
||||
}
|
||||
|
||||
@@ -109,10 +124,19 @@ class WC_REST_Telemetry_Controller extends WC_REST_Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
return array(
|
||||
'platform' => sanitize_text_field( $platform ),
|
||||
'version' => sanitize_text_field( $version ),
|
||||
'last_used' => gmdate( 'c' ),
|
||||
// The installation date could be null from earlier mobile client versions.
|
||||
$installation_date = $request->get_param( 'installation_date' );
|
||||
|
||||
return array_filter(
|
||||
array(
|
||||
'platform' => sanitize_text_field( $platform ),
|
||||
'version' => sanitize_text_field( $version ),
|
||||
'last_used' => gmdate( 'c' ),
|
||||
'installation_date' => isset( $installation_date ) ? get_gmt_from_date( $installation_date, 'c' ) : null,
|
||||
),
|
||||
function( $value ) {
|
||||
return null !== $value;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -123,20 +147,27 @@ class WC_REST_Telemetry_Controller extends WC_REST_Controller {
|
||||
*/
|
||||
public function get_collection_params() {
|
||||
return array(
|
||||
'platform' => array(
|
||||
'platform' => array(
|
||||
'description' => __( 'Platform to track.', 'woocommerce' ),
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'version' => array(
|
||||
'version' => array(
|
||||
'description' => __( 'Platform version to track.', 'woocommerce' ),
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
'installation_date' => array(
|
||||
'description' => __( 'Installation date of the WooCommerce mobile app.', 'woocommerce' ),
|
||||
'required' => false, // For backward compatibility.
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
'validate_callback' => 'rest_validate_request_arg',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ class WC_REST_Customers_V2_Controller extends WC_REST_Customers_V1_Controller {
|
||||
// Format date values.
|
||||
foreach ( $format_date as $key ) {
|
||||
// Date created is stored UTC, date modified is stored WP local time.
|
||||
$datetime = 'date_created' === $key ? get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $data[ $key ]->getTimestamp() ) ) : $data[ $key ];
|
||||
$datetime = 'date_created' === $key && is_subclass_of( $data[ $key ], 'DateTime' ) ? get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $data[ $key ]->getTimestamp() ) ) : $data[ $key ];
|
||||
$data[ $key ] = wc_rest_prepare_date_response( $datetime, false );
|
||||
$data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime );
|
||||
}
|
||||
|
||||
+1
-1
@@ -394,7 +394,7 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
|
||||
'refunded_payment' => array(
|
||||
'description' => __( 'If the payment was refunded via the API.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
'context' => array( 'view' ),
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'meta_data' => array(
|
||||
|
||||
+1
@@ -768,6 +768,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
||||
if ( $creating ) {
|
||||
$object->set_created_via( 'rest-api' );
|
||||
$object->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
|
||||
$object->save();
|
||||
$object->calculate_totals();
|
||||
} else {
|
||||
// If items have changed, recalculate order totals.
|
||||
|
||||
+1
@@ -205,6 +205,7 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller {
|
||||
if ( $creating ) {
|
||||
$object->set_created_via( 'rest-api' );
|
||||
$object->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
|
||||
$object->save();
|
||||
$object->calculate_totals();
|
||||
} else {
|
||||
// If items have changed, recalculate order totals.
|
||||
|
||||
+43
@@ -44,6 +44,10 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
|
||||
'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
),
|
||||
'delete' => array(
|
||||
'description' => __( 'Deletes unused variations.', 'woocommerce' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::CREATABLE,
|
||||
@@ -926,6 +930,40 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all unmatched variations (aka duplicates).
|
||||
*
|
||||
* @param WC_Product $product Variable product.
|
||||
* @return int Number of deleted variations.
|
||||
*/
|
||||
private function delete_unmatched_product_variations( $product ) {
|
||||
$deleted_count = 0;
|
||||
|
||||
if ( ! $product ) {
|
||||
return $deleted_count;
|
||||
}
|
||||
|
||||
$attributes = wc_list_pluck( array_filter( $product->get_attributes(), 'wc_attributes_array_filter_variation' ), 'get_slugs' );
|
||||
|
||||
// Get existing variations so we don't create duplicates.
|
||||
$existing_variations = array_map( 'wc_get_product', $product->get_children() );
|
||||
|
||||
$possible_attribute_combinations = array_reverse( wc_array_cartesian( $attributes ) );
|
||||
|
||||
foreach ( $existing_variations as $existing_variation ) {
|
||||
$matching_attribute_key = array_search( $existing_variation->get_attributes(), $possible_attribute_combinations ); // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
|
||||
if ( $matching_attribute_key !== false ) {
|
||||
// We only want one possible variation for each possible attribute combination.
|
||||
unset( $possible_attribute_combinations[ $matching_attribute_key ] );
|
||||
continue;
|
||||
}
|
||||
$existing_variation->delete( true );
|
||||
$deleted_count ++;
|
||||
}
|
||||
|
||||
return $deleted_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate all variations for a given product.
|
||||
*
|
||||
@@ -947,6 +985,11 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
|
||||
$data_store = $product->get_data_store();
|
||||
$response['count'] = $data_store->create_all_product_variations( $product, Constants::get_constant( 'WC_MAX_LINKED_VARIATIONS' ) );
|
||||
|
||||
if ( isset( $request['delete'] ) && $request['delete'] ) {
|
||||
$deleted_count = $this->delete_unmatched_product_variations( $product );
|
||||
$response['deleted_count'] = $deleted_count;
|
||||
}
|
||||
|
||||
$data_store->sort_all_product_variations( $product->get_id() );
|
||||
|
||||
return rest_ensure_response( $response );
|
||||
|
||||
+14
@@ -432,6 +432,11 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
||||
$product->set_reviews_allowed( $request['reviews_allowed'] );
|
||||
}
|
||||
|
||||
// Post password.
|
||||
if ( isset( $request['post_password'] ) ) {
|
||||
$product->set_post_password( $request['post_password'] );
|
||||
}
|
||||
|
||||
// Virtual.
|
||||
if ( isset( $request['virtual'] ) ) {
|
||||
$product->set_virtual( $request['virtual'] );
|
||||
@@ -1140,6 +1145,11 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
||||
'default' => true,
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'post_password' => array(
|
||||
'description' => __( 'Post password.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'average_rating' => array(
|
||||
'description' => __( 'Reviews average rating.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
@@ -1496,6 +1506,10 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
||||
$data['has_options'] = $product->has_options( $context );
|
||||
}
|
||||
|
||||
if ( in_array( 'post_password', $fields, true ) ) {
|
||||
$data['post_password'] = $product->get_post_password( $context );
|
||||
}
|
||||
|
||||
$post_type_obj = get_post_type_object( $this->post_type );
|
||||
if ( is_post_type_viewable( $post_type_obj ) && $post_type_obj->public ) {
|
||||
$permalink_template_requested = in_array( 'permalink_template', $fields, true );
|
||||
|
||||
+14
@@ -20,6 +20,20 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method {
|
||||
*/
|
||||
protected $fee_cost = '';
|
||||
|
||||
/**
|
||||
* Shipping method cost.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $cost;
|
||||
|
||||
/**
|
||||
* Shipping method type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
+12
-4
@@ -36,6 +36,15 @@ class WC_Shipping_Free_Shipping extends WC_Shipping_Method {
|
||||
*/
|
||||
public $requires = '';
|
||||
|
||||
/**
|
||||
* Ignore discounts.
|
||||
*
|
||||
* If set, free shipping would be available based on pre-discount order amount.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $ignore_discounts;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -153,12 +162,11 @@ class WC_Shipping_Free_Shipping extends WC_Shipping_Method {
|
||||
if ( in_array( $this->requires, array( 'min_amount', 'either', 'both' ), true ) ) {
|
||||
$total = WC()->cart->get_displayed_subtotal();
|
||||
|
||||
if ( WC()->cart->display_prices_including_tax() ) {
|
||||
$total = $total - WC()->cart->get_discount_tax();
|
||||
}
|
||||
|
||||
if ( 'no' === $this->ignore_discounts ) {
|
||||
$total = $total - WC()->cart->get_discount_total();
|
||||
if ( WC()->cart->display_prices_including_tax() ) {
|
||||
$total = $total - WC()->cart->get_discount_tax();
|
||||
}
|
||||
}
|
||||
|
||||
$total = NumberUtil::round( $total, wc_get_price_decimals() );
|
||||
|
||||
+22
@@ -27,6 +27,28 @@ class WC_Shipping_Legacy_Flat_Rate extends WC_Shipping_Method {
|
||||
*/
|
||||
protected $fee_cost = '';
|
||||
|
||||
/**
|
||||
* Shipping method cost.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $cost;
|
||||
|
||||
/**
|
||||
* Shipping method type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Shipping method options.
|
||||
*
|
||||
* @deprecated 2.4.0
|
||||
* @var string
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
|
||||
+16
-1
@@ -20,6 +20,22 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
*/
|
||||
class WC_Shipping_Legacy_Local_Delivery extends WC_Shipping_Local_Pickup {
|
||||
|
||||
/**
|
||||
* Shipping method fee type.
|
||||
*
|
||||
* How to calculate delivery charges.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Allowed post/zip codes for the shipping method.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $codes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@@ -66,7 +82,6 @@ class WC_Shipping_Legacy_Local_Delivery extends WC_Shipping_Local_Pickup {
|
||||
$this->title = $this->get_option( 'title' );
|
||||
$this->type = $this->get_option( 'type' );
|
||||
$this->fee = $this->get_option( 'fee' );
|
||||
$this->type = $this->get_option( 'type' );
|
||||
$this->codes = $this->get_option( 'codes' );
|
||||
$this->availability = $this->get_option( 'availability' );
|
||||
$this->countries = $this->get_option( 'countries' );
|
||||
|
||||
+7
@@ -20,6 +20,13 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
*/
|
||||
class WC_Shipping_Legacy_Local_Pickup extends WC_Shipping_Method {
|
||||
|
||||
/**
|
||||
* Allowed post/zip codes for the shipping method.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $codes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
|
||||
+8
@@ -20,6 +20,14 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
*/
|
||||
class WC_Shipping_Local_Pickup extends WC_Shipping_Method {
|
||||
|
||||
/**
|
||||
* Shipping method cost.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $cost;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
||||
+133
-1
@@ -94,7 +94,7 @@ class WC_Shortcode_Checkout {
|
||||
|
||||
// Logged out customer does not have permission to pay for this order.
|
||||
if ( ! current_user_can( 'pay_for_order', $order_id ) && ! is_user_logged_in() ) {
|
||||
echo '<div class="woocommerce-info">' . esc_html__( 'Please log in to your account below to continue to the payment form.', 'woocommerce' ) . '</div>';
|
||||
wc_print_notice( esc_html__( 'Please log in to your account below to continue to the payment form.', 'woocommerce' ), 'notice' );
|
||||
woocommerce_login_form(
|
||||
array(
|
||||
'redirect' => $order->get_checkout_payment_url(),
|
||||
@@ -171,6 +171,18 @@ class WC_Shortcode_Checkout {
|
||||
}
|
||||
}
|
||||
|
||||
// If we cannot match the order with the current user, ask that they verify their email address.
|
||||
if ( self::guest_should_verify_email( $order, 'order-pay' ) ) {
|
||||
wc_get_template(
|
||||
'checkout/form-verify-email.php',
|
||||
array(
|
||||
'failed_submission' => ! empty( $_POST['email'] ), // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
'verify_url' => $order->get_checkout_payment_url(),
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
WC()->customer->set_props(
|
||||
array(
|
||||
'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null,
|
||||
@@ -258,6 +270,7 @@ class WC_Shortcode_Checkout {
|
||||
|
||||
if ( $order_id > 0 ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( ! $order || ! hash_equals( $order->get_order_key(), $order_key ) ) {
|
||||
$order = false;
|
||||
}
|
||||
@@ -276,6 +289,38 @@ class WC_Shortcode_Checkout {
|
||||
// Empty current cart.
|
||||
wc_empty_cart();
|
||||
|
||||
// If the specified order ID was invalid, we still render the default order received page (which will simply
|
||||
// state that the order was received, but will not output any other details: this makes it harder to probe for
|
||||
// valid order IDs than if we state that the order ID was not recognized).
|
||||
if ( ! $order ) {
|
||||
wc_get_template( 'checkout/thankyou.php', array( 'order' => false ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$order_customer_id = $order->get_customer_id();
|
||||
|
||||
// For non-guest orders, require the user to be logged in before showing this page.
|
||||
if ( $order_customer_id && get_current_user_id() !== $order_customer_id ) {
|
||||
wc_get_template( 'checkout/order-received.php', array( 'order' => false ) );
|
||||
wc_print_notice( esc_html__( 'Please log in to your account to view this order.', 'woocommerce' ), 'notice' );
|
||||
woocommerce_login_form( array( 'redirect' => $order->get_checkout_order_received_url() ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// For guest orders, request they verify their email address (unless we can identify them via the active user session).
|
||||
if ( self::guest_should_verify_email( $order, 'order-received' ) ) {
|
||||
wc_get_template( 'checkout/order-received.php', array( 'order' => false ) );
|
||||
wc_get_template(
|
||||
'checkout/form-verify-email.php',
|
||||
array(
|
||||
'failed_submission' => ! empty( $_POST['email'] ), // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
'verify_url' => $order->get_checkout_order_received_url(),
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, display the thank you (order received) page.
|
||||
wc_get_template( 'checkout/thankyou.php', array( 'order' => $order ) );
|
||||
}
|
||||
|
||||
@@ -317,4 +362,91 @@ class WC_Shortcode_Checkout {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to determine if the user's email address should be verified before rendering either the 'order received' or
|
||||
* 'order pay' pages. This should only be applied to guest orders.
|
||||
*
|
||||
* @param WC_Order $order The order for which a need for email verification is being determined.
|
||||
* @param string $context The context in which email verification is being tested.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function guest_should_verify_email( WC_Order $order, string $context ): bool {
|
||||
$order_email = $order->get_billing_email();
|
||||
$order_customer_id = $order->get_customer_id();
|
||||
|
||||
// If we do not have a billing email for the order (could happen in the order is created manually, or if the
|
||||
// requirement for this has been removed from the checkout flow), email verification does not make sense.
|
||||
if ( empty( $order_email ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No verification step is needed if the user is logged in and is already associated with the order.
|
||||
if ( $order_customer_id && get_current_user_id() === $order_customer_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$email = filter_input( INPUT_POST, 'email' );
|
||||
$nonce = filter_input( INPUT_POST, 'check_submission' );
|
||||
if ( $email && ! wp_verify_nonce( $nonce, 'wc_verify_email' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the grace period within which we do not require any sort of email verification step before rendering
|
||||
* the 'order received' or 'order pay' pages.
|
||||
*
|
||||
* To eliminate the grace period, set to zero (or to a negative value). Note that this filter is not invoked
|
||||
* at all if email verification is deemed to be unnecessary (in other words, it cannot be used to force
|
||||
* verification in *all* cases).
|
||||
*
|
||||
* @since 8.0.0
|
||||
*
|
||||
* @param int $grace_period Time in seconds after an order is placed before email verification may be required.
|
||||
* @param WC_Order $order The order for which this grace period is being assessed.
|
||||
* @param string $context Indicates the context in which we might verify the email address. Typically 'order-pay' or 'order-received'.
|
||||
*/
|
||||
$verification_grace_period = (int) apply_filters( 'woocommerce_order_email_verification_grace_period', 10 * MINUTE_IN_SECONDS, $order, $context );
|
||||
$date_created = $order->get_date_created();
|
||||
|
||||
// We do not need to verify the email address if we are within the grace period immediately following order creation.
|
||||
if (
|
||||
is_a( $date_created, WC_DateTime::class )
|
||||
&& time() - $date_created->getTimestamp() <= $verification_grace_period
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$session = wc()->session;
|
||||
$session_email = '';
|
||||
|
||||
if ( is_a( $session, WC_Session::class ) ) {
|
||||
$customer = $session->get( 'customer' );
|
||||
$session_email = is_array( $customer ) && isset( $customer['email'] ) ? $customer['email'] : '';
|
||||
}
|
||||
|
||||
$session_email_match = $session_email === $order->get_billing_email();
|
||||
$supplied_email_match = isset( $_POST['email'] ) && sanitize_email( wp_unslash( $_POST['email'] ) ?? '' ) === $order->get_billing_email();
|
||||
$can_view_orders = current_user_can( 'read_private_shop_orders' );
|
||||
|
||||
// If we cannot match the order with the current user, the user should verify their email address.
|
||||
$email_verification_required = ! $session_email_match && ! $supplied_email_match && ! $can_view_orders;
|
||||
|
||||
/**
|
||||
* Provides an opportunity to override the (potential) requirement for shoppers to verify their email address
|
||||
* before we show information such as the order summary, or order payment page.
|
||||
*
|
||||
* Note that this hook is not always triggered, therefore it is (for example) unsuitable as a way of forcing
|
||||
* email verification across all order confirmation/order payment scenarios. Instead, the filter primarily
|
||||
* exists as a way to *remove* the email verification step.
|
||||
*
|
||||
* @since 7.9.0
|
||||
*
|
||||
* @param bool $email_verification_required If email verification is required.
|
||||
* @param WC_Order $order The relevant order.
|
||||
* @param string $context The context under which we are performing this check.
|
||||
*/
|
||||
return (bool) apply_filters( 'woocommerce_order_email_verification_required', $email_verification_required, $order, $context );
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -134,8 +134,10 @@ class WC_Shortcode_My_Account {
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( ! $order || ! current_user_can( 'view_order', $order_id ) ) {
|
||||
echo '<div class="woocommerce-error">' . esc_html__( 'Invalid order.', 'woocommerce' ) . ' <a href="' . esc_url( wc_get_page_permalink( 'myaccount' ) ) . '" class="wc-forward">' . esc_html__( 'My account', 'woocommerce' ) . '</a></div>';
|
||||
|
||||
wc_print_notice(
|
||||
esc_html__( 'Invalid order.', 'woocommerce' ) . ' <a href="' . esc_url( wc_get_page_permalink( 'myaccount' ) ) . '" class="wc-forward">' . esc_html__( 'My account', 'woocommerce' ) . '</a>',
|
||||
'error'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user