plugin updates
This commit is contained in:
@@ -231,7 +231,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
||||
'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' ) || \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ? esc_url_raw( admin_url( 'admin.php?page=wc-admin&path=/add-product' ) ) : null,
|
||||
'add_product' => \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,
|
||||
),
|
||||
|
||||
@@ -485,7 +485,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' ) || FeaturesUtil::feature_is_enabled( 'product_block_editor' ) ) {
|
||||
if ( 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.
|
||||
|
||||
@@ -33,7 +33,7 @@ class WC_Helper {
|
||||
* @return string The absolute path to the view file.
|
||||
*/
|
||||
public static function get_view_filename( $view ) {
|
||||
return dirname( __FILE__ ) . "/views/$view";
|
||||
return __DIR__ . "/views/$view";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +119,7 @@ class WC_Helper {
|
||||
$subscriptions_list_data = self::get_subscription_list_data();
|
||||
$subscriptions = array_filter(
|
||||
$subscriptions_list_data,
|
||||
function( $subscription ) {
|
||||
function ( $subscription ) {
|
||||
return ! empty( $subscription['product_key'] );
|
||||
}
|
||||
);
|
||||
@@ -362,9 +362,9 @@ class WC_Helper {
|
||||
*/
|
||||
public static function add_utm_params_to_url_for_subscription_link( $url, $utm_content ) {
|
||||
$utm_params = 'utm_source=subscriptionsscreen&' .
|
||||
'utm_medium=product&' .
|
||||
'utm_campaign=wcaddons&' .
|
||||
'utm_content=' . $utm_content;
|
||||
'utm_medium=product&' .
|
||||
'utm_campaign=wcaddons&' .
|
||||
'utm_content=' . $utm_content;
|
||||
|
||||
// there are already some URL parameters
|
||||
if ( strpos( $url, '?' ) ) {
|
||||
@@ -879,7 +879,8 @@ class WC_Helper {
|
||||
$request = WC_Helper_API::post(
|
||||
'oauth/access_token',
|
||||
array(
|
||||
'body' => array(
|
||||
'timeout' => 30,
|
||||
'body' => array(
|
||||
'request_token' => wp_unslash( $_GET['request_token'] ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
'home_url' => home_url(),
|
||||
),
|
||||
@@ -1554,28 +1555,36 @@ class WC_Helper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connected user's subscription list data.
|
||||
* This is used by the My Subscriptions page.
|
||||
* Get the connected user's subscription list data. Here, we merge connected
|
||||
* subscriptions with locally installed Woo plugins and themes. We also
|
||||
* add in information about available updates.
|
||||
*
|
||||
* Used by the My Subscriptions page.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_subscription_list_data() {
|
||||
// First, connected subscriptions.
|
||||
$subscriptions = self::get_subscriptions();
|
||||
|
||||
// Installed plugins and themes, with or without an active subscription.
|
||||
// Then, installed plugins and themes, with or without an active subscription.
|
||||
$woo_plugins = self::get_local_woo_plugins();
|
||||
$woo_themes = self::get_local_woo_themes();
|
||||
|
||||
// Get the product IDs of the subscriptions.
|
||||
$subscriptions_product_ids = wp_list_pluck( $subscriptions, 'product_id' );
|
||||
|
||||
// Get the site ID.
|
||||
$auth = WC_Helper_Options::get( 'auth' );
|
||||
$site_id = isset( $auth['site_id'] ) ? absint( $auth['site_id'] ) : 0;
|
||||
|
||||
// Installed products without a subscription.
|
||||
// Now, merge installed products without a subscription.
|
||||
foreach ( array_merge( $woo_plugins, $woo_themes ) as $filename => $data ) {
|
||||
if ( in_array( $data['_product_id'], $subscriptions_product_ids, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We add these as subscriptions to the previous connected subscriptions list.
|
||||
$subscriptions[] = array(
|
||||
'product_key' => '',
|
||||
'product_id' => $data['_product_id'],
|
||||
@@ -1587,6 +1596,7 @@ class WC_Helper {
|
||||
'key_type_label' => '',
|
||||
'lifetime' => false,
|
||||
'product_status' => 'publish',
|
||||
// Connections is empty because this is not a connected subscription.
|
||||
'connections' => array(),
|
||||
'expires' => 0,
|
||||
'expired' => true,
|
||||
@@ -1598,11 +1608,13 @@ class WC_Helper {
|
||||
);
|
||||
}
|
||||
|
||||
// Fetch updates so we can refine subscriptions with information about updates.
|
||||
$updates = WC_Helper_Updater::get_update_data();
|
||||
|
||||
// Add local data to merged subscriptions list (both locally installed and purchased).
|
||||
foreach ( $subscriptions as &$subscription ) {
|
||||
$subscription['active'] = in_array( $site_id, $subscription['connections'], true );
|
||||
|
||||
$updates = WC_Helper_Updater::get_update_data();
|
||||
|
||||
$subscription['local'] = self::get_subscription_local_data( $subscription );
|
||||
|
||||
$subscription['has_update'] = false;
|
||||
@@ -1613,12 +1625,17 @@ class WC_Helper {
|
||||
if ( ! empty( $updates[ $subscription['product_id'] ] ) ) {
|
||||
$subscription['version'] = $updates[ $subscription['product_id'] ]['version'];
|
||||
}
|
||||
|
||||
// If the update endpoint returns a URL, we prefer it over the default PluginURI.
|
||||
if ( ! empty( $updates[ $subscription['product_id'] ]['url'] ) ) {
|
||||
$subscription['product_url'] = $updates[ $subscription['product_id'] ]['url'];
|
||||
}
|
||||
}
|
||||
|
||||
// Sort subscriptions by name and expiration date.
|
||||
usort(
|
||||
$subscriptions,
|
||||
function( $a, $b ) {
|
||||
function ( $a, $b ) {
|
||||
$compare_value = strcasecmp( $a['product_name'], $b['product_name'] );
|
||||
if ( 0 === $compare_value ) {
|
||||
return strcasecmp( $a['expires'], $b['expires'] );
|
||||
@@ -1823,7 +1840,7 @@ class WC_Helper {
|
||||
}
|
||||
|
||||
// No more sites available in this subscription.
|
||||
if ( $_sub['sites_max'] && $_sub['sites_active'] >= $_sub['sites_max'] ) {
|
||||
if ( isset( $_sub['maxed'] ) && $_sub['maxed'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1938,7 +1955,7 @@ class WC_Helper {
|
||||
);
|
||||
|
||||
if ( wp_remote_retrieve_response_code( $deactivation_response ) === 200 ) {
|
||||
$deactivated++;
|
||||
++$deactivated;
|
||||
|
||||
/**
|
||||
* Fires when the Helper activates a product successfully.
|
||||
@@ -2011,7 +2028,7 @@ class WC_Helper {
|
||||
|
||||
$product_id = $data['_product_id'];
|
||||
if ( version_compare( $updates[ $product_id ]['version'], $data['Version'], '>' ) ) {
|
||||
$available++;
|
||||
++$available;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2065,6 +2082,7 @@ class WC_Helper {
|
||||
'oauth/me',
|
||||
array(
|
||||
'authenticated' => true,
|
||||
'timeout' => 30,
|
||||
)
|
||||
);
|
||||
|
||||
@@ -2261,6 +2279,42 @@ class WC_Helper {
|
||||
|
||||
return $woo_com_base_url . 'auto-install-init/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve notice for connected store.
|
||||
*
|
||||
* @return array An array containing notice data.
|
||||
*/
|
||||
public static function get_notices() {
|
||||
$cache_key = '_woocommerce_helper_notices';
|
||||
$cached_data = get_transient( $cache_key );
|
||||
|
||||
if ( false !== $cached_data ) {
|
||||
return $cached_data;
|
||||
}
|
||||
|
||||
// Fetch notice data for connected store.
|
||||
$request = WC_Helper_API::get(
|
||||
'notices',
|
||||
array(
|
||||
'authenticated' => true,
|
||||
)
|
||||
);
|
||||
|
||||
if ( 200 !== wp_remote_retrieve_response_code( $request ) ) {
|
||||
set_transient( $cache_key, array(), 15 * MINUTE_IN_SECONDS );
|
||||
return array();
|
||||
}
|
||||
|
||||
$data = json_decode( wp_remote_retrieve_body( $request ), true );
|
||||
|
||||
if ( empty( $data ) || ! is_array( $data ) ) {
|
||||
$data = array();
|
||||
}
|
||||
|
||||
set_transient( $cache_key, $data, 1 * HOUR_IN_SECONDS );
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
WC_Helper::load();
|
||||
|
||||
@@ -562,7 +562,7 @@ class WC_Meta_Box_Order_Data {
|
||||
}
|
||||
|
||||
if ( apply_filters( 'woocommerce_enable_order_notes_field', 'yes' === get_option( 'woocommerce_enable_order_comments', 'yes' ) ) && $order->get_customer_note() ) { // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
echo '<p class="order_note"><strong>' . esc_html( __( 'Customer provided note:', 'woocommerce' ) ) . '</strong> ' . nl2br( esc_html( $order->get_customer_note() ) ) . '</p>';
|
||||
echo '<p class="order_note"><strong>' . esc_html( __( 'Customer provided note:', 'woocommerce' ) ) . '</strong> ' . wp_kses( nl2br( esc_html( $order->get_customer_note() ) ), array() ) . '</p>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
@@ -615,7 +615,7 @@ class WC_Meta_Box_Order_Data {
|
||||
?>
|
||||
<p class="form-field form-field-wide">
|
||||
<label for="customer_note"><?php esc_html_e( 'Customer provided note', 'woocommerce' ); ?>:</label>
|
||||
<textarea rows="1" cols="40" name="customer_note" tabindex="6" id="excerpt" placeholder="<?php esc_attr_e( 'Customer notes about the order', 'woocommerce' ); ?>"><?php echo wp_kses_post( $order->get_customer_note() ); ?></textarea>
|
||||
<textarea rows="1" cols="40" name="customer_note" tabindex="6" id="excerpt" placeholder="<?php esc_attr_e( 'Customer notes about the order', 'woocommerce' ); ?>"><?php echo wp_kses( $order->get_customer_note(), array() ); ?></textarea>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
@@ -197,7 +197,7 @@ class WC_Meta_Box_Product_Data {
|
||||
if ( ! empty( $file_urls ) ) {
|
||||
$file_url_size = count( $file_urls );
|
||||
|
||||
for ( $i = 0; $i < $file_url_size; $i ++ ) {
|
||||
for ( $i = 0; $i < $file_url_size; $i++ ) {
|
||||
if ( ! empty( $file_urls[ $i ] ) ) {
|
||||
$downloads[] = array(
|
||||
'name' => wc_clean( $file_names[ $i ] ),
|
||||
@@ -414,6 +414,9 @@ class WC_Meta_Box_Product_Data {
|
||||
WC_Admin_Meta_Boxes::add_error( $errors->get_error_message() );
|
||||
}
|
||||
|
||||
// Remove _product_template_id for products that were created with the new product editor.
|
||||
$product->delete_meta_data( '_product_template_id' );
|
||||
|
||||
/**
|
||||
* Set props before save.
|
||||
*
|
||||
|
||||
@@ -71,10 +71,10 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
echo '<input type="hidden" name="_original_stock" value="' . esc_attr( wc_stock_amount( $product_object->get_stock_quantity( 'edit' ) ) ) . '" />';
|
||||
|
||||
$backorder_args = array(
|
||||
'id' => '_backorders',
|
||||
'value' => $product_object->get_backorders( 'edit' ),
|
||||
'label' => __( 'Allow backorders?', 'woocommerce' ),
|
||||
'options' => wc_get_product_backorder_options(),
|
||||
'id' => '_backorders',
|
||||
'value' => $product_object->get_backorders( 'edit' ),
|
||||
'label' => __( 'Allow backorders?', 'woocommerce' ),
|
||||
'options' => wc_get_product_backorder_options(),
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -188,19 +188,27 @@ class WC_Settings_Payment_Gateways extends WC_Settings_Page {
|
||||
echo wp_kses_post( $gateway->get_method_description() );
|
||||
break;
|
||||
case 'action':
|
||||
if ( wc_string_to_bool( $gateway->enabled ) ) {
|
||||
/* Translators: %s Payment gateway name. */
|
||||
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 (
|
||||
// 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();
|
||||
$setup_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . strtolower( $gateway->id ) );
|
||||
// Override the behaviour for WooPayments plugin.
|
||||
if (
|
||||
// Keep old brand name for backwards compatibility.
|
||||
( 'WooCommerce Payments' === $method_title || 'WooPayments' === $method_title ) &&
|
||||
class_exists( 'WC_Payments_Account' )
|
||||
) {
|
||||
if ( ! WooCommercePayments::is_connected() || WooCommercePayments::is_account_partially_onboarded() ) {
|
||||
// The CTA text and label is "Finish set up" if the account is not connected or not completely onboarded.
|
||||
$setup_url = WC_Payments_Account::get_connect_url(); // Plugin will handle the redirection to the connect page or directly to the provider (e.g. Stripe).
|
||||
/* Translators: %s Payment gateway name. */
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Set up the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Finish set up', 'woocommerce' ) . '</a>';
|
||||
} else {
|
||||
$setup_url = admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=' . strtolower( $gateway->id ) );
|
||||
// If the account is fully onboarded, the CTA text and label is "Manage" regardless gateway is enabled or not.
|
||||
/* Translators: %s Payment gateway name. */
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Manage the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Manage', 'woocommerce' ) . '</a>';
|
||||
}
|
||||
} elseif ( wc_string_to_bool( $gateway->enabled ) ) {
|
||||
/* Translators: %s Payment gateway name. */
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Manage the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Manage', 'woocommerce' ) . '</a>';
|
||||
} else {
|
||||
/* Translators: %s Payment gateway name. */
|
||||
echo '<a class="button alignright" aria-label="' . esc_attr( sprintf( __( 'Set up the "%s" payment method', 'woocommerce' ), $method_title ) ) . '" href="' . esc_url( $setup_url ) . '">' . esc_html__( 'Finish set up', 'woocommerce' ) . '</a>';
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@ defined( 'ABSPATH' ) || exit;
|
||||
<div id="key-fields" class="settings-panel">
|
||||
<h2><?php esc_html_e( 'Key details', 'woocommerce' ); ?></h2>
|
||||
|
||||
<div class="inline notice">
|
||||
<ul class="advice">
|
||||
<li><?php esc_html_e( 'API keys open up access to potentially sensitive information. Only share them with organizations you trust.', 'woocommerce' ); ?></li>
|
||||
<li><?php esc_html_e( 'Stick to one key per client: this makes it easier to revoke access in the future for a single client, without causing disruption for others.', 'woocommerce' ); ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="key_id" value="<?php echo esc_attr( $key_id ); ?>" />
|
||||
|
||||
<table id="api-keys-options" class="form-table">
|
||||
@@ -24,6 +31,9 @@ defined( 'ABSPATH' ) || exit;
|
||||
</th>
|
||||
<td class="forminp">
|
||||
<input maxlength="200" id="key_description" type="text" class="input-text regular-input" value="<?php echo esc_attr( $key_data['description'] ); ?>" />
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Add a meaningful description, including a note of the person, company or app you are sharing the key with.', 'woocommerce' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top">
|
||||
@@ -72,6 +82,9 @@ defined( 'ABSPATH' ) || exit;
|
||||
<option value="<?php echo esc_attr( $permission_id ); ?>" <?php selected( $key_data['permissions'], $permission_id, true ); ?>><?php echo esc_html( $permission_name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<p class="conditional description" data-depends-on="#key_permissions" data-show-if-equals="write">
|
||||
<?php esc_html_e( 'Write-only keys do not prevent clients from seeing information about the entities they are updating.', 'woocommerce' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
|
||||
use Automattic\WooCommerce\Utilities\RestApiUtil;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
@@ -873,10 +874,38 @@ if ( 0 < $mu_plugins_count ) :
|
||||
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . ( $_page['block_required'] ? sprintf( esc_html__( 'Page does not contain the %1$s shortcode or the %2$s block.', 'woocommerce' ), esc_html( $_page['shortcode'] ), esc_html( $_page['block'] ) ) : sprintf( esc_html__( 'Page does not contain the %s shortcode.', 'woocommerce' ), esc_html( $_page['shortcode'] ) ) ) . '</mark>'; /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */
|
||||
$found_error = true;
|
||||
}
|
||||
|
||||
// Warn merchants if both the shortcode and block are present, which will be a confusing shopper experience.
|
||||
if ( $_page['shortcode_present'] && $_page['block_present'] ) {
|
||||
/* Translators: %1$s: shortcode text, %2$s: block slug. */
|
||||
echo '<mark class="error"><span class="dashicons dashicons-warning"></span> ' . sprintf( esc_html__( 'Page contains both the %1$s shortcode and the %2$s block.', 'woocommerce' ), esc_html( $_page['shortcode'] ), esc_html( $_page['block'] ) ) . '</mark>'; /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */
|
||||
$found_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $found_error ) {
|
||||
echo '<mark class="yes">#' . absint( $_page['page_id'] ) . ' - ' . esc_html( str_replace( home_url(), '', get_permalink( $_page['page_id'] ) ) ) . '</mark>';
|
||||
|
||||
$additional_info = '';
|
||||
|
||||
// We only state the used type on the Checkout and the Cart page.
|
||||
if ( in_array( $_page['block'], array( 'woocommerce/checkout', 'woocommerce/cart' ), true ) ) {
|
||||
// We check first if, in a blocks theme, the template content does not load the page content.
|
||||
if ( CartCheckoutUtils::is_overriden_by_custom_template_content( $_page['block'] ) ) {
|
||||
$additional_info = __( "This page's content is overridden by custom template content", 'woocommerce' );
|
||||
} elseif ( $_page['shortcode_present'] ) {
|
||||
/* Translators: %1$s: shortcode text. */
|
||||
$additional_info = sprintf( __( 'Contains the <strong>%1$s</strong> shortcode', 'woocommerce' ), esc_html( $_page['shortcode'] ) );
|
||||
} elseif ( $_page['block_present'] ) {
|
||||
/* Translators: %1$s: block slug. */
|
||||
$additional_info = sprintf( __( 'Contains the <strong>%1$s</strong> block', 'woocommerce' ), esc_html( $_page['block'] ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $additional_info ) ) {
|
||||
$additional_info = '<mark class="no"> - <span class="dashicons dashicons-info"></span> ' . $additional_info . '</mark>';
|
||||
}
|
||||
}
|
||||
|
||||
echo '<mark class="yes">#' . absint( $_page['page_id'] ) . ' - ' . esc_html( str_replace( home_url(), '', get_permalink( $_page['page_id'] ) ) ) . '</mark>' . $additional_info; /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
|
||||
}
|
||||
|
||||
echo '</td></tr>';
|
||||
@@ -969,7 +998,7 @@ if ( 0 < $mu_plugins_count ) :
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="wc_status_table widefat" cellspacing="0">
|
||||
<table class="wc_status_table widefat" id="status-table-templates" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="3" data-export-label="Templates"><h2><?php esc_html_e( 'Templates', 'woocommerce' ); ?><?php echo wc_help_tip( esc_html__( 'This section shows any files that are overriding the default WooCommerce template pages.', 'woocommerce' ) ); ?></h2></th>
|
||||
|
||||
@@ -24,6 +24,6 @@ $theme = wp_get_theme();
|
||||
</p>
|
||||
<p class="submit">
|
||||
<a class="button-primary" href="https://woocommerce.com/document/template-structure/" target="_blank"><?php esc_html_e( 'Learn more about templates', 'woocommerce' ); ?></a>
|
||||
<a class="button-primary" href="<?php echo esc_url( admin_url( 'admin.php?page=wc-status' ) ); ?>" target="_blank"><?php esc_html_e( 'View affected templates', 'woocommerce' ); ?></a>
|
||||
<a class="button-primary" href="<?php echo esc_url( admin_url( 'admin.php?page=wc-status#status-table-templates' ) ); ?>" target="_blank"><?php esc_html_e( 'View affected templates', 'woocommerce' ); ?></a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -69,6 +69,14 @@ class WC_Autoloader {
|
||||
return;
|
||||
}
|
||||
|
||||
// The Legacy REST API was removed in WooCommerce 9.0, but some servers still have
|
||||
// the includes/class-wc-api.php file after they upgrade, which causes a fatal error when executing
|
||||
// "class_exists('WC_API')". This will prevent this error, while still making the class visible
|
||||
// when it's provided by the WooCommerce Legacy REST API plugin.
|
||||
if ( 'wc_api' === $class ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file = $this->get_file_name_from_class( $class );
|
||||
$path = '';
|
||||
|
||||
|
||||
@@ -169,8 +169,7 @@ class WC_Cache_Helper {
|
||||
$redirect_url = add_query_arg( $wp->query_string, '', $redirect_url );
|
||||
}
|
||||
|
||||
$redirect_url = add_query_arg( 'v', $location_hash, remove_query_arg( 'v', $redirect_url ) );
|
||||
|
||||
$redirect_url = add_query_arg( 'v', $location_hash, remove_query_arg( array( 'v', 'add-to-cart' ), $redirect_url ) );
|
||||
wp_safe_redirect( esc_url_raw( $redirect_url ), 307 );
|
||||
exit;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* @version 3.0.0
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\DataBase\Migrations\CustomOrderTable\CLIRunner;
|
||||
use Automattic\WooCommerce\DataBase\Migrations\CustomOrderTable\CLIRunner as CustomOrdersTableCLIRunner;
|
||||
use Automattic\WooCommerce\Internal\ProductAttributesLookup\CLIRunner as ProductAttributesLookupCLIRunner;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
@@ -26,13 +27,13 @@ class WC_CLI {
|
||||
* Load command files.
|
||||
*/
|
||||
private function includes() {
|
||||
require_once dirname( __FILE__ ) . '/cli/class-wc-cli-runner.php';
|
||||
require_once dirname( __FILE__ ) . '/cli/class-wc-cli-rest-command.php';
|
||||
require_once dirname( __FILE__ ) . '/cli/class-wc-cli-tool-command.php';
|
||||
require_once dirname( __FILE__ ) . '/cli/class-wc-cli-update-command.php';
|
||||
require_once dirname( __FILE__ ) . '/cli/class-wc-cli-tracker-command.php';
|
||||
require_once dirname( __FILE__ ) . '/cli/class-wc-cli-com-command.php';
|
||||
require_once dirname( __FILE__ ) . '/cli/class-wc-cli-com-extension-command.php';
|
||||
require_once __DIR__ . '/cli/class-wc-cli-runner.php';
|
||||
require_once __DIR__ . '/cli/class-wc-cli-rest-command.php';
|
||||
require_once __DIR__ . '/cli/class-wc-cli-tool-command.php';
|
||||
require_once __DIR__ . '/cli/class-wc-cli-update-command.php';
|
||||
require_once __DIR__ . '/cli/class-wc-cli-tracker-command.php';
|
||||
require_once __DIR__ . '/cli/class-wc-cli-com-command.php';
|
||||
require_once __DIR__ . '/cli/class-wc-cli-com-extension-command.php';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,8 +46,10 @@ class WC_CLI {
|
||||
WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_Tracker_Command::register_commands' );
|
||||
WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_COM_Command::register_commands' );
|
||||
WP_CLI::add_hook( 'after_wp_load', 'WC_CLI_COM_Extension_Command::register_commands' );
|
||||
$cli_runner = wc_get_container()->get( CLIRunner::class );
|
||||
$cli_runner = wc_get_container()->get( CustomOrdersTableCLIRunner::class );
|
||||
WP_CLI::add_hook( 'after_wp_load', array( $cli_runner, 'register_commands' ) );
|
||||
$cli_runner = wc_get_container()->get( ProductAttributesLookupCLIRunner::class );
|
||||
WP_CLI::add_hook( 'after_wp_load', fn() => \WP_CLI::add_command( 'wc palt', $cli_runner ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -597,7 +597,7 @@ class WC_Emails {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders any additional fields captured during block based checkout.
|
||||
* Renders any additional fields captured during block-based checkout.
|
||||
*
|
||||
* @param WC_Order $order Order instance.
|
||||
* @param bool $sent_to_admin If email is sent to admin.
|
||||
@@ -634,7 +634,7 @@ class WC_Emails {
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders any additional address fields captured during block based checkout.
|
||||
* Renders any additional address fields captured during block-based checkout.
|
||||
*
|
||||
* @param string $address_type Address type.
|
||||
* @param WC_Order $order Order instance.
|
||||
|
||||
@@ -298,6 +298,11 @@ class WC_Frontend_Scripts {
|
||||
'deps' => array( 'jquery', 'woocommerce' ),
|
||||
'version' => $version,
|
||||
),
|
||||
'wc-account-i18n' => array(
|
||||
'src' => self::get_asset_url( 'assets/js/frontend/account-i18n' . $suffix . '.js' ),
|
||||
'deps' => array( 'jquery' ),
|
||||
'version' => $version,
|
||||
),
|
||||
'wc-password-strength-meter' => array(
|
||||
'src' => self::get_asset_url( 'assets/js/frontend/password-strength-meter' . $suffix . '.js' ),
|
||||
'deps' => array( 'jquery', 'password-strength-meter' ),
|
||||
@@ -389,6 +394,9 @@ class WC_Frontend_Scripts {
|
||||
self::enqueue_script( 'wc-password-strength-meter' );
|
||||
}
|
||||
}
|
||||
if ( is_account_page() ) {
|
||||
self::enqueue_script( 'wc-account-i18n' );
|
||||
}
|
||||
if ( is_checkout() ) {
|
||||
self::enqueue_script( 'wc-checkout' );
|
||||
}
|
||||
|
||||
@@ -255,8 +255,9 @@ class WC_Install {
|
||||
'8.9.1' => array(
|
||||
'wc_update_891_create_plugin_autoinstall_history_option',
|
||||
),
|
||||
'9.0.0' => array(
|
||||
'wc_update_900_add_launch_your_store_tour_option',
|
||||
'9.1.0' => array(
|
||||
'wc_update_910_add_launch_your_store_tour_option',
|
||||
'wc_update_910_remove_obsolete_user_meta',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -183,10 +183,10 @@ class WC_Logger implements WC_Logger_Interface {
|
||||
* @param array $context Additional information for log handlers.
|
||||
* @param object $handler The handler object, such as WC_Log_Handler_File. Available since 5.3.
|
||||
*/
|
||||
$message = apply_filters( 'woocommerce_logger_log_message', $message, $level, $context, $handler );
|
||||
$filtered_message = apply_filters( 'woocommerce_logger_log_message', $message, $level, $context, $handler );
|
||||
|
||||
if ( null !== $message ) {
|
||||
$handler->handle( $timestamp, $level, $message, $context );
|
||||
if ( null !== $filtered_message ) {
|
||||
$handler->handle( $timestamp, $level, $filtered_message, $context );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Admin\Features\Features;
|
||||
|
||||
/**
|
||||
* Post types Class.
|
||||
@@ -108,6 +109,7 @@ class WC_Post_Types {
|
||||
'not_found' => __( 'No categories found', 'woocommerce' ),
|
||||
'item_link' => __( 'Product Category Link', 'woocommerce' ),
|
||||
'item_link_description' => __( 'A link to a product category.', 'woocommerce' ),
|
||||
'template_name' => _x( 'Products by Category', 'Template name', 'woocommerce' ),
|
||||
),
|
||||
'show_in_rest' => true,
|
||||
'show_ui' => true,
|
||||
@@ -153,6 +155,7 @@ class WC_Post_Types {
|
||||
'not_found' => __( 'No tags found', 'woocommerce' ),
|
||||
'item_link' => __( 'Product Tag Link', 'woocommerce' ),
|
||||
'item_link_description' => __( 'A link to a product tag.', 'woocommerce' ),
|
||||
'template_name' => _x( 'Products by Tag', 'Template name', 'woocommerce' ),
|
||||
),
|
||||
'show_in_rest' => true,
|
||||
'show_ui' => true,
|
||||
@@ -369,6 +372,73 @@ class WC_Post_Types {
|
||||
)
|
||||
);
|
||||
|
||||
// Register the product form post type wne the feature is enabled.
|
||||
if ( Features::is_enabled( 'product-editor-template-system' ) ) {
|
||||
register_post_type(
|
||||
'product_form',
|
||||
/**
|
||||
* Allow developers to customize the product form post type registration arguments.
|
||||
*
|
||||
* @since 9.1.0
|
||||
* @param array $args The default post type registration arguments.
|
||||
*/
|
||||
apply_filters(
|
||||
'woocommerce_register_post_type_product_form',
|
||||
array(
|
||||
'labels'
|
||||
=> array(
|
||||
'name' => __( 'Product Forms', 'woocommerce' ),
|
||||
'singular_name' => __( 'Product Form', 'woocommerce' ),
|
||||
'all_items' => __( 'All Product Form', 'woocommerce' ),
|
||||
'menu_name' => _x( 'Product Forms', 'Admin menu name', 'woocommerce' ),
|
||||
'add_new' => __( 'Add New', 'woocommerce' ),
|
||||
'add_new_item' => __( 'Add new product form', 'woocommerce' ),
|
||||
'edit' => __( 'Edit', 'woocommerce' ),
|
||||
'edit_item' => __( 'Edit product form', 'woocommerce' ),
|
||||
'new_item' => __( 'New product form', 'woocommerce' ),
|
||||
'view_item' => __( 'View product form', 'woocommerce' ),
|
||||
'view_items' => __( 'View product forms', 'woocommerce' ),
|
||||
'search_items' => __( 'Search product forms', 'woocommerce' ),
|
||||
'not_found' => __( 'No product forms found', 'woocommerce' ),
|
||||
'not_found_in_trash' => __( 'No product forms found in trash', 'woocommerce' ),
|
||||
'parent' => __( 'Parent product form', 'woocommerce' ),
|
||||
'featured_image' => __( 'Product form image', 'woocommerce' ),
|
||||
'set_featured_image' => __( 'Set product form image', 'woocommerce' ),
|
||||
'remove_featured_image' => __( 'Remove product form image', 'woocommerce' ),
|
||||
'use_featured_image' => __( 'Use as product form image', 'woocommerce' ),
|
||||
'insert_into_item' => __( 'Insert into product form', 'woocommerce' ),
|
||||
'uploaded_to_this_item' => __( 'Uploaded to this product form', 'woocommerce' ),
|
||||
'filter_items_list' => __( 'Filter product forms', 'woocommerce' ),
|
||||
'items_list_navigation' => __( 'Product forms navigation', 'woocommerce' ),
|
||||
'items_list' => __( 'Product forms list', 'woocommerce' ),
|
||||
'item_link' => __( 'Product form Link', 'woocommerce' ),
|
||||
'item_link_description' => __( 'A link to a product form.', 'woocommerce' ),
|
||||
),
|
||||
'description' => __( 'This is where you can set up product forms for various product types in your dashboard.', 'woocommerce' ),
|
||||
'public' => true,
|
||||
'menu_icon' => 'dashicons-forms',
|
||||
'capability_type' => 'product',
|
||||
'map_meta_cap' => true,
|
||||
'publicly_queryable' => true,
|
||||
'hierarchical' => false, // Hierarchical causes memory issues - WP loads all records!
|
||||
'rewrite' => $permalinks['product_rewrite_slug'] ? array(
|
||||
'slug' => $permalinks['product_rewrite_slug'],
|
||||
'with_front' => false,
|
||||
'feeds' => true,
|
||||
) : false,
|
||||
'query_var' => true,
|
||||
'supports' => $supports,
|
||||
'has_archive' => $has_archive,
|
||||
'show_in_rest' => true,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'exclude_from_search' => true,
|
||||
'show_in_nav_menus' => false,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
register_post_type(
|
||||
'product_variation',
|
||||
apply_filters(
|
||||
|
||||
@@ -601,7 +601,7 @@ class WC_Product_Variable extends WC_Product {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sync a variable product with it's children. These sync functions sync
|
||||
* Sync a variable product with its children. These sync functions sync
|
||||
* upwards (from child to parent) when the variation is saved.
|
||||
*
|
||||
* @param WC_Product|int $product Product object or ID for which you wish to sync.
|
||||
|
||||
@@ -272,6 +272,12 @@ class WC_Regenerate_Images {
|
||||
$imagedata['width'] = $imagedata['sizes']['full']['width'];
|
||||
}
|
||||
|
||||
// The result of the earlier wp_get_attachment_metadata call is filterable, so we may not have height or
|
||||
// width data at this point.
|
||||
if ( ! isset( $imagedata['height'] ) || ! isset( $imagedata['width'] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array(
|
||||
'width' => $imagedata['width'],
|
||||
'height' => $imagedata['height'],
|
||||
|
||||
@@ -34,6 +34,22 @@ class WC_REST_Authentication {
|
||||
*/
|
||||
protected $auth_method = '';
|
||||
|
||||
/**
|
||||
* Provides access to the global WC_REST_Authentication instance.
|
||||
*
|
||||
* @internal
|
||||
* @return self
|
||||
*/
|
||||
public static function instance(): self {
|
||||
static $instance;
|
||||
|
||||
if ( ! isset( $instance ) ) {
|
||||
$instance = new self();
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize authentication actions.
|
||||
*/
|
||||
@@ -582,11 +598,39 @@ class WC_REST_Authentication {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updated API Key last access datetime.
|
||||
* Updates the `last_access` field for the API key associated with the current request.
|
||||
*
|
||||
* This method tries to disambiguate 'primary' API requests from any programmatic REST
|
||||
* API requests made internally.
|
||||
*
|
||||
* @param WP_REST_Request $request The request currently being processed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function update_last_access() {
|
||||
private function update_last_access( $request ) {
|
||||
global $wp;
|
||||
global $wpdb;
|
||||
|
||||
// Lots of (programmatically) created REST API requests may be handled within the same process.
|
||||
// In most cases, however, we do not want to record the last access time for each of these.
|
||||
$do_not_record = true;
|
||||
|
||||
// Try to detect if the REST API request actively being processed matches the current WP request.
|
||||
if ( is_a( $wp, WP::class ) && is_a( $request, WP_REST_Request::class ) ) {
|
||||
$actual_http_request = trim( $wp->request, '/' );
|
||||
$api_request_in_progress = trim( $request->get_route(), '/' );
|
||||
|
||||
// Remove the REST API route prefix (normally 'wp-json') for easier comparison.
|
||||
$rest_prefix = trailingslashit( rest_get_url_prefix() );
|
||||
|
||||
if ( str_starts_with( $actual_http_request, $rest_prefix ) ) {
|
||||
$actual_http_request = substr( $actual_http_request, strlen( $rest_prefix ) );
|
||||
}
|
||||
|
||||
// Recommend recording the last access time only if the actual WP request and the current
|
||||
// API request being processed are a match.
|
||||
$do_not_record = $actual_http_request !== $api_request_in_progress;
|
||||
}
|
||||
/**
|
||||
* This filter enables the exclusion of the most recent access time from being logged for REST API calls.
|
||||
*
|
||||
@@ -596,7 +640,7 @@ class WC_REST_Authentication {
|
||||
*
|
||||
* @since 7.7.0
|
||||
*/
|
||||
if ( apply_filters( 'woocommerce_disable_rest_api_access_log', false, $this->user->key_id, $this->user->user_id ) ) {
|
||||
if ( apply_filters( 'woocommerce_disable_rest_api_access_log', $do_not_record, $this->user->key_id, $this->user->user_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -643,11 +687,11 @@ class WC_REST_Authentication {
|
||||
}
|
||||
|
||||
// Register last access.
|
||||
$this->update_last_access();
|
||||
$this->update_last_access( $request );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
new WC_REST_Authentication();
|
||||
WC_REST_Authentication::instance();
|
||||
|
||||
@@ -1162,5 +1162,3 @@ class WC_Tracker {
|
||||
return get_option( 'woocommerce_mobile_app_usage' );
|
||||
}
|
||||
}
|
||||
|
||||
WC_Tracker::init();
|
||||
|
||||
@@ -533,7 +533,10 @@ class WC_Webhook extends WC_Legacy_Webhook {
|
||||
// Check for a success, which is a 2xx, 301 or 302 Response Code.
|
||||
if ( intval( $response_code ) >= 200 && intval( $response_code ) < 303 ) {
|
||||
$this->set_failure_count( 0 );
|
||||
$this->save();
|
||||
|
||||
if ( 0 !== $this->get_id() ) {
|
||||
$this->save();
|
||||
}
|
||||
} else {
|
||||
$this->failed_delivery();
|
||||
}
|
||||
@@ -557,7 +560,9 @@ class WC_Webhook extends WC_Legacy_Webhook {
|
||||
$this->set_failure_count( ++$failures );
|
||||
}
|
||||
|
||||
$this->save();
|
||||
if ( 0 !== $this->get_id() ) {
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,7 @@ final class WooCommerce {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = '9.0.0';
|
||||
public $version = '9.1.2';
|
||||
|
||||
/**
|
||||
* WooCommerce Schema version.
|
||||
@@ -682,6 +682,7 @@ final class WooCommerce {
|
||||
|
||||
if ( $this->is_request( 'cron' ) && 'yes' === get_option( 'woocommerce_allow_tracking', 'no' ) ) {
|
||||
include_once WC_ABSPATH . 'includes/class-wc-tracker.php';
|
||||
WC_Tracker::init();
|
||||
}
|
||||
|
||||
$this->theme_support_includes();
|
||||
|
||||
@@ -118,7 +118,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
throw new Exception( __( 'Invalid coupon.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$coupon_id = $coupon->get_id();
|
||||
$coupon_id = $coupon->get_id();
|
||||
$limit_usage_to_x_items = get_post_meta( $coupon_id, 'limit_usage_to_x_items', true );
|
||||
$coupon->set_props(
|
||||
array(
|
||||
'code' => $post_object->post_title,
|
||||
@@ -131,11 +132,11 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
'amount' => get_post_meta( $coupon_id, 'coupon_amount', true ),
|
||||
'usage_count' => get_post_meta( $coupon_id, 'usage_count', true ),
|
||||
'individual_use' => 'yes' === get_post_meta( $coupon_id, 'individual_use', true ),
|
||||
'product_ids' => array_filter( (array) explode( ',', get_post_meta( $coupon_id, 'product_ids', true ) ) ),
|
||||
'excluded_product_ids' => array_filter( (array) explode( ',', get_post_meta( $coupon_id, 'exclude_product_ids', true ) ) ),
|
||||
'product_ids' => $this->get_coupon_meta_as_array( $coupon_id, 'product_ids' ),
|
||||
'excluded_product_ids' => $this->get_coupon_meta_as_array( $coupon_id, 'exclude_product_ids' ),
|
||||
'usage_limit' => get_post_meta( $coupon_id, 'usage_limit', true ),
|
||||
'usage_limit_per_user' => get_post_meta( $coupon_id, 'usage_limit_per_user', true ),
|
||||
'limit_usage_to_x_items' => 0 < get_post_meta( $coupon_id, 'limit_usage_to_x_items', true ) ? get_post_meta( $coupon_id, 'limit_usage_to_x_items', true ) : null,
|
||||
'limit_usage_to_x_items' => $limit_usage_to_x_items > 0 ? $limit_usage_to_x_items : null,
|
||||
'free_shipping' => 'yes' === get_post_meta( $coupon_id, 'free_shipping', true ),
|
||||
'product_categories' => array_filter( (array) get_post_meta( $coupon_id, 'product_categories', true ) ),
|
||||
'excluded_product_categories' => array_filter( (array) get_post_meta( $coupon_id, 'exclude_product_categories', true ) ),
|
||||
@@ -151,6 +152,24 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
do_action( 'woocommerce_coupon_loaded', $coupon );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a metadata value that is stored as either a string consisting of a comma-separated list of values
|
||||
* or as a serialized array.
|
||||
*
|
||||
* WooCommerce always stores the coupon product ids as a comma-separated string, but it seems that
|
||||
* some plugins mistakenly change these to an array.
|
||||
*
|
||||
* @param int $coupon_id The coupon id.
|
||||
* @param string $meta_key The meta key to get.
|
||||
* @return array The metadata value as an array, with empty values removed.
|
||||
*/
|
||||
private function get_coupon_meta_as_array( $coupon_id, string $meta_key ) {
|
||||
$meta_value = get_post_meta( $coupon_id, $meta_key, true );
|
||||
return array_filter(
|
||||
is_array( $meta_value ) ? $meta_value : explode( ',', $meta_value )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a coupon in the database.
|
||||
*
|
||||
@@ -190,6 +209,12 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
$this->update_post_meta( $coupon );
|
||||
$coupon->apply_changes();
|
||||
delete_transient( 'rest_api_coupons_type_count' );
|
||||
|
||||
// The `coupon_id_from_code` entry in the object cache must not exist when the coupon is not published, otherwise the coupon will remain available for use.
|
||||
if ( 'publish' !== $coupon->get_status() ) {
|
||||
wp_cache_delete( WC_Cache_Helper::get_cache_prefix( 'coupons' ) . 'coupon_id_from_code_' . $coupon->get_code(), 'coupons' );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_update_coupon', $coupon->get_id(), $coupon );
|
||||
}
|
||||
|
||||
@@ -440,7 +465,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
*/
|
||||
public function get_usage_by_user_id( &$coupon, $user_id ) {
|
||||
global $wpdb;
|
||||
$usage_count = $wpdb->get_var(
|
||||
$usage_count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %s;",
|
||||
$coupon->get_id(),
|
||||
@@ -461,7 +486,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
*/
|
||||
public function get_usage_by_email( &$coupon, $email ) {
|
||||
global $wpdb;
|
||||
$usage_count = $wpdb->get_var(
|
||||
$usage_count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT( meta_id ) FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_used_by' AND meta_value = %s;",
|
||||
$coupon->get_id(),
|
||||
@@ -485,7 +510,6 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
return $wpdb->get_var(
|
||||
$this->get_tentative_usage_query_for_user( $coupon_id, $user_aliases )
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,8 +661,8 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
|
||||
$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 ' . $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(
|
||||
$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 $wpdb->posts
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
use Automattic\WooCommerce\Internal\Utilities\Users;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
@@ -340,17 +341,36 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
||||
* @return WC_Order|false
|
||||
*/
|
||||
public function get_last_order( &$customer ) {
|
||||
//phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
|
||||
// Try to fetch the last order placed by this customer.
|
||||
$last_order_id = Users::get_site_user_meta( $customer->get_id(), 'wc_last_order', true );
|
||||
$last_customer_order = false;
|
||||
|
||||
if ( ! empty( $last_order_id ) ) {
|
||||
$last_customer_order = wc_get_order( $last_order_id );
|
||||
}
|
||||
|
||||
// "Unset" the last order ID if the order is associated with another customer. Unsetting is done by making it an
|
||||
// empty string, for compatibility with the declared types of the following filter hook.
|
||||
if (
|
||||
! $last_customer_order instanceof WC_Order
|
||||
|| intval( $last_customer_order->get_customer_id() ) !== intval( $customer->get_id() )
|
||||
) {
|
||||
$last_order_id = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the id of the last order from a given customer.
|
||||
*
|
||||
* @param string @last_order_id The last order id as retrieved from the database.
|
||||
* @param WC_Customer The customer whose last order id is being retrieved.
|
||||
* @since 4.9.1
|
||||
*
|
||||
* @param string $last_order_id The last order id as retrieved from the database.
|
||||
* @param WC_Customer $customer The customer whose last order id is being retrieved.
|
||||
*
|
||||
* @return string The actual last order id to use.
|
||||
*/
|
||||
$last_order_id = apply_filters(
|
||||
'woocommerce_customer_get_last_order',
|
||||
get_user_meta( $customer->get_id(), '_last_order', true ),
|
||||
$last_order_id,
|
||||
$customer
|
||||
);
|
||||
//phpcs:enable WooCommerce.Commenting.CommentHooks.MissingSinceComment
|
||||
@@ -385,7 +405,7 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
||||
);
|
||||
}
|
||||
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
update_user_meta( $customer->get_id(), '_last_order', $last_order_id );
|
||||
Users::update_site_user_meta( $customer->get_id(), 'wc_last_order', $last_order_id );
|
||||
}
|
||||
|
||||
if ( ! $last_order_id ) {
|
||||
@@ -405,7 +425,7 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
||||
public function get_order_count( &$customer ) {
|
||||
$count = apply_filters(
|
||||
'woocommerce_customer_get_order_count',
|
||||
get_user_meta( $customer->get_id(), '_order_count', true ),
|
||||
Users::get_site_user_meta( $customer->get_id(), 'wc_order_count', true ),
|
||||
$customer
|
||||
);
|
||||
|
||||
@@ -436,7 +456,7 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
||||
}
|
||||
//phpcs:enable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
|
||||
update_user_meta( $customer->get_id(), '_order_count', $count );
|
||||
Users::update_site_user_meta( $customer->get_id(), 'wc_order_count', $count );
|
||||
}
|
||||
|
||||
return absint( $count );
|
||||
@@ -452,7 +472,7 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
||||
public function get_total_spent( &$customer ) {
|
||||
$spent = apply_filters(
|
||||
'woocommerce_customer_get_total_spent',
|
||||
get_user_meta( $customer->get_id(), '_money_spent', true ),
|
||||
Users::get_site_user_meta( $customer->get_id(), 'wc_money_spent', true ),
|
||||
$customer
|
||||
);
|
||||
|
||||
@@ -499,7 +519,7 @@ class WC_Customer_Data_Store extends WC_Data_Store_WP implements WC_Customer_Dat
|
||||
if ( ! $spent ) {
|
||||
$spent = 0;
|
||||
}
|
||||
update_user_meta( $customer->get_id(), '_money_spent', $spent );
|
||||
Users::update_site_user_meta( $customer->get_id(), 'wc_money_spent', $spent );
|
||||
}
|
||||
|
||||
return wc_format_decimal( $spent, 2 );
|
||||
|
||||
@@ -138,6 +138,12 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
||||
if ( $id && ! is_wp_error( $id ) ) {
|
||||
$product->set_id( $id );
|
||||
|
||||
// get the post object so that we can set the status
|
||||
// to the correct value; it is possible that the status was
|
||||
// changed by the woocommerce_new_product_data filter above.
|
||||
$post_object = get_post( $product->get_id() );
|
||||
$product->set_status( $post_object->post_status );
|
||||
|
||||
$this->update_post_meta( $product, true );
|
||||
$this->update_terms( $product, true );
|
||||
$this->update_visibility( $product, true );
|
||||
@@ -272,7 +278,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
||||
|
||||
$product->apply_changes();
|
||||
|
||||
do_action( 'woocommerce_update_product', $product->get_id(), $product );
|
||||
// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
do_action( 'woocommerce_update_product', $product->get_id(), $product, $changes );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1236,7 +1243,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
||||
|
||||
do_action( 'product_variation_linked', $variation_id );
|
||||
|
||||
$count ++;
|
||||
++$count;
|
||||
|
||||
if ( $limit > 0 && $count >= $limit ) {
|
||||
break;
|
||||
|
||||
@@ -166,23 +166,6 @@ 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.
|
||||
*/
|
||||
|
||||
@@ -416,7 +416,7 @@ class WC_Email extends WC_Settings_API {
|
||||
* @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 );
|
||||
return apply_filters( 'woocommerce_email_additional_content_' . $this->id, $this->format_string( $this->get_option( 'additional_content' ) ), $this->object, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,9 +602,23 @@ class WC_Email extends WC_Settings_API {
|
||||
*/
|
||||
public function style_inline( $content ) {
|
||||
if ( in_array( $this->get_content_type(), array( 'text/html', 'multipart/alternative' ), true ) ) {
|
||||
$css = '';
|
||||
$css .= $this->get_must_use_css_styles();
|
||||
$css .= "\n";
|
||||
|
||||
ob_start();
|
||||
wc_get_template( 'emails/email-styles.php' );
|
||||
$css = apply_filters( 'woocommerce_email_styles', ob_get_clean(), $this );
|
||||
$css .= ob_get_clean();
|
||||
|
||||
/**
|
||||
* Provides an opportunity to filter the CSS styles included in e-mails.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*
|
||||
* @param string $css CSS code.
|
||||
* @param \WC_Email $email E-mail instance.
|
||||
*/
|
||||
$css = apply_filters( 'woocommerce_email_styles', $css, $this );
|
||||
|
||||
$css_inliner_class = CssInliner::class;
|
||||
|
||||
@@ -632,6 +646,29 @@ class WC_Email extends WC_Settings_API {
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns CSS styles that should be included with all HTML e-mails, regardless of theme specific customizations.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_must_use_css_styles(): string {
|
||||
$css = <<<'EOF'
|
||||
|
||||
/*
|
||||
* Temporary measure until e-mail clients more properly support the correct styles.
|
||||
* See https://github.com/woocommerce/woocommerce/pull/47738.
|
||||
*/
|
||||
.screen-reader-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
EOF;
|
||||
|
||||
return $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if emogrifier library is supported.
|
||||
*
|
||||
|
||||
@@ -7,6 +7,7 @@ if ( ! function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
'activity-panels' => true,
|
||||
'analytics' => true,
|
||||
'product-block-editor' => true,
|
||||
'experimental-blocks' => false,
|
||||
'coupons' => true,
|
||||
'core-profiler' => true,
|
||||
'customize-store' => true,
|
||||
@@ -20,7 +21,6 @@ if ( ! function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
'minified-js' => false,
|
||||
'mobile-app-banner' => true,
|
||||
'navigation' => true,
|
||||
'new-product-management-experience' => false,
|
||||
'onboarding' => true,
|
||||
'onboarding-tasks' => true,
|
||||
'pattern-toolkit-full-composability' => false,
|
||||
@@ -29,6 +29,7 @@ if ( ! function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
'remote-inbox-notifications' => true,
|
||||
'remote-free-extensions' => true,
|
||||
'payment-gateway-suggestions' => true,
|
||||
'printful' => false,
|
||||
'settings' => false,
|
||||
'shipping-label-banner' => true,
|
||||
'subscriptions' => true,
|
||||
@@ -38,7 +39,7 @@ if ( ! function_exists( 'wc_admin_get_feature_config' ) ) {
|
||||
'wc-pay-promotion' => true,
|
||||
'wc-pay-welcome-page' => true,
|
||||
'async-product-editor-category-field' => false,
|
||||
'launch-your-store' => false,
|
||||
'launch-your-store' => true,
|
||||
'product-editor-template-system' => false,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -64,13 +64,14 @@ class WC_REST_Customer_Downloads_V1_Controller extends WC_REST_Controller {
|
||||
* @return WP_Error|boolean
|
||||
*/
|
||||
public function get_items_permissions_check( $request ) {
|
||||
$customer = get_user_by( 'id', (int) $request['customer_id'] );
|
||||
$customer = new WC_Customer( (int) $request['customer_id'] );
|
||||
$customer_id = $customer->get_id();
|
||||
|
||||
if ( ! $customer ) {
|
||||
if ( ! $customer_id ) {
|
||||
return new WP_Error( 'woocommerce_rest_customer_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
if ( ! wc_rest_check_user_permissions( 'read', $customer->get_id() ) ) {
|
||||
if ( ! wc_rest_check_user_permissions( 'read', $customer_id ) ) {
|
||||
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -1951,7 +1951,7 @@ class WC_REST_Products_V1_Controller extends WC_REST_Posts_Controller {
|
||||
),
|
||||
'stock_quantity' => array(
|
||||
'description' => __( 'Stock quantity.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'type' => has_filter( 'woocommerce_stock_amount', 'intval' ) ? 'integer' : 'number',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'in_stock' => array(
|
||||
|
||||
@@ -586,7 +586,7 @@ class WC_REST_Order_Refunds_V2_Controller extends WC_REST_Orders_V2_Controller {
|
||||
),
|
||||
'rate_id' => array(
|
||||
'description' => __( 'Tax rate ID.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
|
||||
@@ -930,6 +930,11 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
||||
$this->maybe_set_item_props( $item, array( 'name', 'quantity', 'total', 'subtotal', 'tax_class' ), $posted );
|
||||
$this->maybe_set_item_meta_data( $item, $posted );
|
||||
|
||||
if ( 'update' === $action ) {
|
||||
require_once WC_ABSPATH . 'includes/admin/wc-admin-functions.php';
|
||||
wc_maybe_adjust_line_item_product_stock( $item );
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
@@ -1619,7 +1624,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
||||
),
|
||||
'rate_id' => array(
|
||||
'description' => __( 'Tax rate ID.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
'type' => 'integer',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
|
||||
@@ -462,8 +462,12 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
||||
protected function get_attribute_taxonomy_name( $slug, $product ) {
|
||||
// Format slug so it matches attributes of the product.
|
||||
$slug = wc_attribute_taxonomy_slug( $slug );
|
||||
$attributes = $product->get_attributes();
|
||||
$attribute = false;
|
||||
$attributes = array_combine(
|
||||
array_map( 'wc_sanitize_taxonomy_name', array_keys( $product->get_attributes() ) ),
|
||||
array_values( $product->get_attributes() )
|
||||
);
|
||||
|
||||
$attribute = false;
|
||||
|
||||
// pa_ attributes.
|
||||
if ( isset( $attributes[ wc_attribute_taxonomy_name( $slug ) ] ) ) {
|
||||
@@ -1904,7 +1908,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
|
||||
),
|
||||
'stock_quantity' => array(
|
||||
'description' => __( 'Stock quantity.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'type' => has_filter( 'woocommerce_stock_amount', 'intval' ) ? 'integer' : 'number',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'in_stock' => array(
|
||||
|
||||
@@ -231,13 +231,16 @@ class WC_REST_Shipping_Zone_Methods_V2_Controller extends WC_REST_Shipping_Zones
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires after a product review is deleted via the REST API.
|
||||
* Fires after a shipping zone is deleted via the REST API.
|
||||
*
|
||||
* @param object $method
|
||||
* @param WP_REST_Response $response The response data.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
* @since 9.1.0
|
||||
*
|
||||
* @param WC_Shipping_Method $method The shipping zone method being deleted.
|
||||
* @param WC_Shipping_Zone $zone The shipping zone the method belonged to.
|
||||
* @param WP_REST_Response $response The response data.
|
||||
* @param WP_REST_Request $request The request sent to the API.
|
||||
*/
|
||||
do_action( 'rest_delete_product_review', $method, $response, $request );
|
||||
do_action( 'woocommerce_rest_delete_shipping_zone_method', $method, $zone, $response, $request );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
@@ -1407,36 +1407,39 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller {
|
||||
$shortcode_required = false;
|
||||
$block_present = false;
|
||||
$block_required = false;
|
||||
$page = false;
|
||||
|
||||
// Page checks.
|
||||
if ( $page_id ) {
|
||||
$page_set = true;
|
||||
}
|
||||
if ( get_post( $page_id ) ) {
|
||||
$page_exists = true;
|
||||
}
|
||||
if ( 'publish' === get_post_status( $page_id ) ) {
|
||||
$page_visible = true;
|
||||
$page = get_post( $page_id );
|
||||
|
||||
if ( $page ) {
|
||||
$page_exists = true;
|
||||
|
||||
if ( 'publish' === $page->post_status ) {
|
||||
$page_visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shortcode checks.
|
||||
if ( $values['shortcode'] && get_post( $page_id ) ) {
|
||||
if ( $values['shortcode'] && $page ) {
|
||||
$shortcode_required = true;
|
||||
$page = get_post( $page_id );
|
||||
if ( strstr( $page->post_content, $values['shortcode'] ) ) {
|
||||
if ( has_shortcode( $page->post_content, trim( $values['shortcode'], '[]' ) ) ) {
|
||||
$shortcode_present = true;
|
||||
}
|
||||
|
||||
// Compatibility with the classic shortcode block which can be used instead of shortcodes.
|
||||
if ( ! $shortcode_present && ( 'woocommerce/checkout' === $values['block'] || 'woocommerce/cart' === $values['block'] ) ) {
|
||||
$shortcode_present = has_block( 'woocommerce/classic-shortcode', $page->post_content );
|
||||
}
|
||||
}
|
||||
|
||||
// Block checks.
|
||||
if ( $values['block'] && get_post( $page_id ) ) {
|
||||
if ( $values['block'] && $page ) {
|
||||
$block_required = true;
|
||||
$block_present = WC_Blocks_Utils::has_block_in_page( $page_id, $values['block'] );
|
||||
|
||||
// Compatibility with the classic shortcode block which can be used instead of shortcodes.
|
||||
if ( ! $block_present && ( 'woocommerce/checkout' === $values['block'] || 'woocommerce/cart' === $values['block'] ) ) {
|
||||
$block_present = WC_Blocks_Utils::has_block_in_page( $page_id, 'woocommerce/classic-shortcode', true );
|
||||
}
|
||||
$block_present = has_block( $values['block'], $page->post_content );
|
||||
}
|
||||
|
||||
// Wrap up our findings into an output array.
|
||||
|
||||
@@ -24,4 +24,61 @@ class WC_REST_Product_Shipping_Classes_Controller extends WC_REST_Product_Shippi
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'wc/v3';
|
||||
|
||||
/**
|
||||
* Register the routes for product reviews.
|
||||
*/
|
||||
public function register_routes() {
|
||||
parent::register_routes();
|
||||
|
||||
register_rest_route(
|
||||
$this->namespace,
|
||||
'/' . $this->rest_base . '/slug-suggestion',
|
||||
array(
|
||||
'args' => array(
|
||||
'name' => array(
|
||||
'description' => __( 'Suggest a slug for the term.', 'woocommerce' ),
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'methods' => WP_REST_Server::READABLE,
|
||||
'callback' => array( $this, 'suggest_slug' ),
|
||||
'permission_callback' => array( $this, 'get_item_permissions_check' ),
|
||||
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
|
||||
),
|
||||
'schema' => array( $this, 'get_public_item_schema' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback fuction for the slug-suggestion endpoint.
|
||||
*
|
||||
* @param WP_REST_Request $request Full details about the request.
|
||||
* @return string The suggested slug.
|
||||
*/
|
||||
public function suggest_slug( $request ) {
|
||||
$name = $request['name'];
|
||||
$slug = sanitize_title( $name ); // potential slug.
|
||||
$term = get_term_by( 'slug', $slug, $this->taxonomy );
|
||||
|
||||
/*
|
||||
* If the term exists, creates a unique slug
|
||||
* based on the name provided.
|
||||
* Otherwise, returns the sanitized name.
|
||||
*/
|
||||
if ( isset( $term->slug ) ) {
|
||||
/*
|
||||
* Pass a Term object that has only the taxonomy property,
|
||||
* to induce the wp_unique_term_slug() function to generate a unique slug.
|
||||
* Otherwise, the function will return the same slug.
|
||||
* @see https://core.trac.wordpress.org/browser/tags/6.5/src/wp-includes/taxonomy.php#L3130
|
||||
* @see https://github.com/WordPress/wordpress-develop/blob/a1b1e0339eb6dfa72a30933cac2a1c6ad2bbfe96/src/wp-includes/taxonomy.php#L3078-L3156
|
||||
*/
|
||||
$slug = wp_unique_term_slug( $slug, (object) array( 'taxonomy' => $this->taxonomy ) );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $slug );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,7 +1131,7 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
|
||||
),
|
||||
'stock_quantity' => array(
|
||||
'description' => __( 'Stock quantity.', 'woocommerce' ),
|
||||
'type' => 'integer',
|
||||
'type' => has_filter( 'woocommerce_stock_amount', 'intval' ) ? 'integer' : 'number',
|
||||
'context' => array( 'view', 'edit' ),
|
||||
),
|
||||
'stock_status' => array(
|
||||
|
||||
@@ -108,9 +108,6 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method {
|
||||
// Remove whitespace from string.
|
||||
$sum = preg_replace( '/\s+/', '', $sum );
|
||||
|
||||
// Removed thousand separator.
|
||||
$sum = str_replace( wc_get_price_thousand_separator(), '', $sum );
|
||||
|
||||
// Remove locale from string.
|
||||
$sum = str_replace( $decimals, '.', $sum );
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ class WC_Products_Tracking {
|
||||
add_action( 'load-edit.php', array( $this, 'track_products_view' ), 10 );
|
||||
add_action( 'load-edit-tags.php', array( $this, 'track_categories_and_tags_view' ), 10, 2 );
|
||||
add_action( 'edit_post', array( $this, 'track_product_updated' ), 10, 2 );
|
||||
add_action( 'wp_after_insert_post', array( $this, 'track_product_published' ), 10, 4 );
|
||||
add_action( 'woocommerce_new_product', array( $this, 'track_product_published' ), 10, 3 );
|
||||
add_action( 'woocommerce_update_product', array( $this, 'track_product_published' ), 10, 3 );
|
||||
add_action( 'created_product_cat', array( $this, 'track_product_category_created' ) );
|
||||
add_action( 'edited_product_cat', array( $this, 'track_product_category_updated' ) );
|
||||
add_action( 'add_meta_boxes_product', array( $this, 'track_product_updated_client_side' ), 10 );
|
||||
@@ -125,7 +126,7 @@ class WC_Products_Tracking {
|
||||
}
|
||||
|
||||
/* phpcs:disable WooCommerce.Commenting.CommentHooks.MissingHookComment */
|
||||
$source = apply_filters( 'woocommerce_product_source', self::TRACKS_SOURCE );
|
||||
$source = apply_filters( 'woocommerce_product_source', self::is_importing() ? 'import' : self::TRACKS_SOURCE );
|
||||
$properties = array(
|
||||
'product_id' => $product_id,
|
||||
'source' => $source,
|
||||
@@ -303,24 +304,21 @@ class WC_Products_Tracking {
|
||||
/**
|
||||
* Send a Tracks event when a product is published.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param WP_Post $post Post object.
|
||||
* @param bool $update Whether this is an existing post being updated.
|
||||
* @param null|WP_Post $post_before Null for new posts, the WP_Post object prior
|
||||
* to the update for updated posts.
|
||||
* @param int $product_id Product ID.
|
||||
* @param WC_Product $product Product object.
|
||||
* @param array $changes Product changes.
|
||||
*/
|
||||
public function track_product_published( $post_id, $post, $update, $post_before ) {
|
||||
public function track_product_published( $product_id, $product, $changes = null ) {
|
||||
if (
|
||||
'product' !== $post->post_type ||
|
||||
'publish' !== $post->post_status ||
|
||||
( $post_before && 'publish' === $post_before->post_status )
|
||||
! isset( $product ) ||
|
||||
'product' !== $product->post_type ||
|
||||
'publish' !== $product->get_status( 'edit' ) ||
|
||||
( $changes && ! isset( $changes['status'] ) )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product = wc_get_product( $post_id );
|
||||
|
||||
$product_type_options = self::get_product_type_options( $post_id );
|
||||
$product_type_options = self::get_product_type_options( $product_id );
|
||||
$product_type_options_string = self::get_product_type_options_string( $product_type_options );
|
||||
|
||||
$properties = array(
|
||||
@@ -334,14 +332,14 @@ class WC_Products_Tracking {
|
||||
'is_virtual' => $product->is_virtual() ? 'yes' : 'no',
|
||||
'manage_stock' => $product->get_manage_stock() ? 'yes' : 'no',
|
||||
'menu_order' => $product->get_menu_order() ? 'yes' : 'no',
|
||||
'product_id' => $post_id,
|
||||
'product_id' => $product_id,
|
||||
'product_gallery' => count( $product->get_gallery_image_ids() ),
|
||||
'product_image' => $product->get_image_id() ? 'yes' : 'no',
|
||||
'product_type' => $product->get_type(),
|
||||
'product_type_options' => $product_type_options_string,
|
||||
'purchase_note' => $product->get_purchase_note() ? 'yes' : 'no',
|
||||
'sale_price' => $product->get_sale_price() ? 'yes' : 'no',
|
||||
'source' => apply_filters( 'woocommerce_product_source', self::TRACKS_SOURCE ),
|
||||
'source' => apply_filters( 'woocommerce_product_source', self::is_importing() ? 'import' : self::TRACKS_SOURCE ),
|
||||
'short_description' => $product->get_short_description() ? 'yes' : 'no',
|
||||
'tags' => count( $product->get_tag_ids() ),
|
||||
'upsells' => ! empty( $product->get_upsell_ids() ) ? 'yes' : 'no',
|
||||
@@ -546,4 +544,19 @@ class WC_Products_Tracking {
|
||||
}
|
||||
WCAdminAssets::register_script( 'wp-admin-scripts', 'add-term-tracking', false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current process is importing products.
|
||||
*
|
||||
* @return bool True if importing, false otherwise.
|
||||
*/
|
||||
private function is_importing() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Missing
|
||||
// Check if the current request is a product import.
|
||||
if ( isset( $_POST['action'] ) && 'woocommerce_do_ajax_product_import' === $_POST['action'] ) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
// phpcs:enable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,6 +633,16 @@ function wc_update_attribute( $id, $args ) {
|
||||
|
||||
$args['id'] = $attribute ? $attribute->id : 0;
|
||||
|
||||
// When updating an existing attribute, populate any undefined args with the existing value.
|
||||
// This prevents those values from being reset to their respective defaults.
|
||||
if ( $args['id'] ) {
|
||||
$args['has_archives'] = $args['has_archives'] ?? $attribute->has_archives;
|
||||
$args['name'] = $args['name'] ?? $attribute->name;
|
||||
$args['order_by'] = $args['order_by'] ?? $attribute->order_by;
|
||||
$args['slug'] = $args['slug'] ?? $attribute->slug;
|
||||
$args['type'] = $args['type'] ?? $attribute->type;
|
||||
}
|
||||
|
||||
if ( $args['id'] && empty( $args['name'] ) ) {
|
||||
$args['name'] = $attribute->name;
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ function wc_get_default_shipping_method_for_package( $key, $package, $chosen_met
|
||||
* If the customer has selected local pickup, keep it selected if it's still in the package. We don't want to auto
|
||||
* toggle between shipping and pickup even if available shipping methods are changed.
|
||||
*
|
||||
* This is important for block based checkout where there is an explicit toggle between shipping and pickup.
|
||||
* This is important for block-based checkout where there is an explicit toggle between shipping and pickup.
|
||||
*/
|
||||
$local_pickup_method_ids = LocalPickupUtils::get_local_pickup_method_ids();
|
||||
$is_local_pickup_chosen = in_array( $chosen_method_id, $local_pickup_method_ids, true );
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
|
||||
use Automattic\WooCommerce\Internal\Utilities\Users;
|
||||
use Automattic\WooCommerce\Utilities\StringUtil;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
@@ -484,9 +485,9 @@ function wc_delete_shop_order_transients( $order = 0 ) {
|
||||
// Clear customer's order related caches.
|
||||
if ( is_a( $order, 'WC_Order' ) ) {
|
||||
$order_id = $order->get_id();
|
||||
delete_user_meta( $order->get_customer_id(), '_money_spent' );
|
||||
delete_user_meta( $order->get_customer_id(), '_order_count' );
|
||||
delete_user_meta( $order->get_customer_id(), '_last_order' );
|
||||
Users::delete_site_user_meta( $order->get_customer_id(), 'wc_money_spent' );
|
||||
Users::delete_site_user_meta( $order->get_customer_id(), 'wc_order_count' );
|
||||
Users::delete_site_user_meta( $order->get_customer_id(), 'wc_last_order' );
|
||||
} else {
|
||||
$order_id = 0;
|
||||
}
|
||||
@@ -1063,6 +1064,7 @@ function wc_get_order_note( $data ) {
|
||||
'content' => $data->comment_content,
|
||||
'customer_note' => (bool) get_comment_meta( $data->comment_ID, 'is_customer_note', true ),
|
||||
'added_by' => __( 'WooCommerce', 'woocommerce' ) === $data->comment_author ? 'system' : $data->comment_author,
|
||||
'order_id' => absint( $data->comment_post_ID ),
|
||||
),
|
||||
$data
|
||||
);
|
||||
@@ -1183,5 +1185,20 @@ function wc_create_order_note( $order_id, $note, $is_customer_note = false, $add
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
function wc_delete_order_note( $note_id ) {
|
||||
return wp_delete_comment( $note_id, true );
|
||||
$note = wc_get_order_note( $note_id );
|
||||
if ( $note && wp_delete_comment( $note_id, true ) ) {
|
||||
/**
|
||||
* Action hook fired after an order note is deleted.
|
||||
*
|
||||
* @param int $note_id Order note ID.
|
||||
* @param stdClass $note Object with the deleted order note details.
|
||||
*
|
||||
* @since 9.1.0
|
||||
*/
|
||||
do_action( 'woocommerce_order_note_deleted', $note_id, $note );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -296,6 +296,7 @@ function wc_increase_stock_levels( $order_id ) {
|
||||
|
||||
$item_name = $product->get_formatted_name();
|
||||
$new_stock = wc_update_product_stock( $product, $item_stock_reduced, 'increase' );
|
||||
$old_stock = $new_stock - $item_stock_reduced;
|
||||
|
||||
if ( is_wp_error( $new_stock ) ) {
|
||||
/* translators: %s item name. */
|
||||
@@ -306,7 +307,18 @@ function wc_increase_stock_levels( $order_id ) {
|
||||
$item->delete_meta_data( '_reduced_stock' );
|
||||
$item->save();
|
||||
|
||||
$changes[] = $item_name . ' ' . ( $new_stock - $item_stock_reduced ) . '→' . $new_stock;
|
||||
$changes[] = $item_name . ' ' . $old_stock . '→' . $new_stock;
|
||||
|
||||
/**
|
||||
* Fires when stock restored to a specific line item
|
||||
*
|
||||
* @since 9.1.0
|
||||
* @param WC_Order_Item_Product $item Order item data.
|
||||
* @param int $new_stock New stock.
|
||||
* @param int $old_stock Old stock.
|
||||
* @param WC_Order $order Order data.
|
||||
*/
|
||||
do_action( 'woocommerce_restore_order_item_stock', $item, $new_stock, $old_stock, $order );
|
||||
}
|
||||
|
||||
if ( $changes ) {
|
||||
|
||||
@@ -3994,4 +3994,27 @@ function wc_get_pay_buttons() {
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the product archive title to the title of the shop page. Fallback to
|
||||
* 'Shop' if the shop page doesn't exist.
|
||||
*
|
||||
* @param string $post_type_name Post type 'name' label.
|
||||
* @param string $post_type Post type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function wc_update_product_archive_title( $post_type_name, $post_type ) {
|
||||
if ( is_shop() && 'product' === $post_type ) {
|
||||
$shop_page_title = get_the_title( wc_get_page_id( 'shop' ) );
|
||||
if ( $shop_page_title ) {
|
||||
return $shop_page_title;
|
||||
}
|
||||
|
||||
return __( 'Shop', 'woocommerce' );
|
||||
}
|
||||
|
||||
return $post_type_name;
|
||||
}
|
||||
add_filter( 'post_type_archive_title', 'wc_update_product_archive_title', 10, 2 );
|
||||
|
||||
// phpcs:enable Generic.Commenting.Todo.TaskFound
|
||||
|
||||
@@ -2728,6 +2728,53 @@ function wc_update_891_create_plugin_autoinstall_history_option() {
|
||||
/**
|
||||
* Add woocommerce_show_lys_tour.
|
||||
*/
|
||||
function wc_update_900_add_launch_your_store_tour_option() {
|
||||
function wc_update_910_add_launch_your_store_tour_option() {
|
||||
add_option( 'woocommerce_show_lys_tour', 'yes' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user meta associated with the keys '_last_order', '_order_count' and '_money_spent'.
|
||||
*
|
||||
* New keys are now used for these, to improve compatibility with multisite networks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function wc_update_910_remove_obsolete_user_meta() {
|
||||
global $wpdb;
|
||||
|
||||
$deletions = $wpdb->query( "
|
||||
DELETE FROM $wpdb->usermeta
|
||||
WHERE meta_key IN (
|
||||
'_last_order',
|
||||
'_order_count',
|
||||
'_money_spent'
|
||||
)
|
||||
" );
|
||||
|
||||
$logger = wc_get_logger();
|
||||
|
||||
if ( null === $logger ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( false === $deletions ) {
|
||||
$logger->notice(
|
||||
'During the update to 9.1.0, WooCommerce attempted to remove user meta with the keys "_last_order", "_order_count" and "_money_spent" but was unable to do so.',
|
||||
array(
|
||||
'source' => 'wc-updater',
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$logger->info(
|
||||
sprintf(
|
||||
1 === $deletions
|
||||
? 'During the update to 9.1.0, WooCommerce removed %d user meta row associated with the meta keys "_last_order", "_order_count" or "_money_spent".'
|
||||
: 'During the update to 9.1.0, WooCommerce removed %d user meta rows associated with the meta keys "_last_order", "_order_count" or "_money_spent".',
|
||||
number_format_i18n( $deletions )
|
||||
),
|
||||
array(
|
||||
'source' => 'wc-updater',
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
|
||||
use Automattic\WooCommerce\Internal\Utilities\Users;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
@@ -280,9 +281,9 @@ function wc_update_new_customer_past_orders( $customer_id ) {
|
||||
|
||||
if ( $complete ) {
|
||||
update_user_meta( $customer_id, 'paying_customer', 1 );
|
||||
update_user_meta( $customer_id, '_order_count', '' );
|
||||
update_user_meta( $customer_id, '_money_spent', '' );
|
||||
delete_user_meta( $customer_id, '_last_order' );
|
||||
Users::update_site_user_meta( $customer_id, 'wc_order_count', '' );
|
||||
Users::update_site_user_meta( $customer_id, 'wc_money_spent', '' );
|
||||
Users::delete_site_user_meta( $customer_id, 'wc_last_order' );
|
||||
}
|
||||
|
||||
return $linked;
|
||||
@@ -367,7 +368,7 @@ function wc_customer_bought_product( $customer_email, $user_id, $product_id ) {
|
||||
$user_id_clause = 'OR o.customer_id = ' . absint( $user_id );
|
||||
}
|
||||
$sql = "
|
||||
SELECT im.meta_value FROM $order_table AS o
|
||||
SELECT DISTINCT im.meta_value FROM $order_table AS o
|
||||
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON o.id = i.order_id
|
||||
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
|
||||
WHERE o.status IN ('" . implode( "','", $statuses ) . "')
|
||||
@@ -379,7 +380,7 @@ AND ( o.billing_email IN ('" . implode( "','", $customer_data ) . "') $user_id_c
|
||||
} else {
|
||||
$result = $wpdb->get_col(
|
||||
"
|
||||
SELECT im.meta_value FROM {$wpdb->posts} AS p
|
||||
SELECT DISTINCT im.meta_value FROM {$wpdb->posts} AS p
|
||||
INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
|
||||
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id
|
||||
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
|
||||
|
||||
@@ -75,6 +75,11 @@ add_action( 'woocommerce_webhook_process_delivery', 'wc_webhook_process_delivery
|
||||
*/
|
||||
function wc_deliver_webhook_async( $webhook_id, $arg ) {
|
||||
$webhook = new WC_Webhook( $webhook_id );
|
||||
|
||||
if ( 0 === $webhook->get_id() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$webhook->deliver( $arg );
|
||||
}
|
||||
add_action( 'woocommerce_deliver_webhook_async', 'wc_deliver_webhook_async', 10, 2 );
|
||||
|
||||
Reference in New Issue
Block a user