PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.3
WooCommerce v10.9.3
10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / wc-update-functions.php
woocommerce / includes Last commit date
abstracts 4 weeks ago admin 5 days ago blocks 11 months ago cli 4 weeks ago customizer 4 months ago data-stores 4 weeks ago emails 4 days ago export 1 year ago gateways 4 weeks ago import 3 months ago integrations 2 months ago interfaces 4 months ago legacy 4 months ago libraries 4 weeks ago log-handlers 1 year ago payment-tokens 5 years ago product-usage 1 year ago queue 4 months ago react-admin 4 weeks ago rest-api 4 weeks ago shipping 3 months ago shortcodes 4 weeks ago theme-support 2 years ago tracks 4 weeks ago traits 5 years ago walkers 5 years ago wccom-site 4 weeks ago widgets 2 months ago class-wc-ajax.php 4 weeks ago class-wc-auth.php 1 year ago class-wc-autoloader.php 9 months ago class-wc-background-emailer.php 2 months ago class-wc-background-updater.php 5 years ago class-wc-brands-brand-settings-manager.php 1 year ago class-wc-brands-coupons.php 1 year ago class-wc-brands.php 4 weeks ago class-wc-breadcrumb.php 4 months ago class-wc-cache-helper.php 4 weeks ago class-wc-cart-fees.php 2 years ago class-wc-cart-session.php 3 months ago class-wc-cart-totals.php 11 months ago class-wc-cart.php 4 weeks ago class-wc-checkout.php 4 weeks ago class-wc-cli.php 10 months ago class-wc-comments.php 4 months ago class-wc-countries.php 2 months ago class-wc-coupon.php 2 months ago class-wc-customer-download-log.php 5 years ago class-wc-customer-download.php 1 year ago class-wc-customer.php 4 weeks ago class-wc-data-exception.php 8 years ago class-wc-data-store.php 3 years ago class-wc-datetime.php 4 years ago class-wc-deprecated-action-hooks.php 2 years ago class-wc-deprecated-filter-hooks.php 3 months ago class-wc-discounts.php 11 months ago class-wc-download-handler.php 4 weeks ago class-wc-emails.php 4 weeks ago class-wc-embed.php 1 year ago class-wc-form-handler.php 3 months ago class-wc-frontend-scripts.php 2 months ago class-wc-geo-ip.php 9 months ago class-wc-geolite-integration.php 6 years ago class-wc-geolocation.php 2 months ago class-wc-https.php 3 years ago class-wc-install.php 5 days ago class-wc-integrations.php 5 years ago class-wc-log-levels.php 2 years ago class-wc-logger.php 4 months ago class-wc-meta-data.php 4 weeks ago class-wc-order-factory.php 2 months ago class-wc-order-item-coupon.php 4 years ago class-wc-order-item-fee.php 5 months ago class-wc-order-item-meta.php 4 years ago class-wc-order-item-product.php 4 weeks ago class-wc-order-item-shipping.php 5 months ago class-wc-order-item-tax.php 4 weeks ago class-wc-order-item.php 4 weeks ago class-wc-order-query.php 4 weeks ago class-wc-order-refund.php 1 year ago class-wc-order.php 4 weeks ago class-wc-payment-gateways.php 4 weeks ago class-wc-payment-tokens.php 3 years ago class-wc-post-data.php 2 months ago class-wc-post-types.php 4 weeks ago class-wc-privacy-background-process.php 1 year ago class-wc-privacy-erasers.php 10 months ago class-wc-privacy-exporters.php 4 years ago class-wc-privacy.php 1 year ago class-wc-product-attribute.php 4 months ago class-wc-product-download.php 4 months ago class-wc-product-external.php 1 year ago class-wc-product-factory.php 3 months ago class-wc-product-grouped.php 3 months ago class-wc-product-query.php 4 months ago class-wc-product-simple.php 11 months ago class-wc-product-variable.php 4 weeks ago class-wc-product-variation.php 4 weeks ago class-wc-query.php 4 weeks ago class-wc-rate-limiter.php 4 years ago class-wc-regenerate-images-request.php 3 years ago class-wc-regenerate-images.php 2 years ago class-wc-register-wp-admin-settings.php 5 years ago class-wc-rest-authentication.php 2 years ago class-wc-rest-exception.php 5 years ago class-wc-session-handler.php 3 months ago class-wc-shipping-rate.php 1 year ago class-wc-shipping-zone.php 5 years ago class-wc-shipping-zones.php 7 months ago class-wc-shipping.php 2 months ago class-wc-shortcodes.php 1 year ago class-wc-structured-data.php 4 weeks ago class-wc-tax.php 2 months ago class-wc-template-loader.php 7 months ago class-wc-tracker.php 4 weeks ago class-wc-validation.php 2 years ago class-wc-webhook.php 4 weeks ago class-woocommerce.php 4 days ago wc-account-functions.php 4 weeks ago wc-attribute-functions.php 4 weeks ago wc-brands-functions.php 1 year ago wc-cart-functions.php 4 weeks ago wc-conditional-functions.php 11 months ago wc-core-functions.php 2 months ago wc-coupon-functions.php 5 months ago wc-deprecated-functions.php 4 weeks ago wc-formatting-functions.php 4 weeks ago wc-interactivity-api-functions.php 2 months ago wc-notice-functions.php 5 months ago wc-order-functions.php 1 month ago wc-order-item-functions.php 3 years ago wc-order-step-logger-functions.php 4 months ago wc-page-functions.php 1 month ago wc-product-functions.php 2 months ago wc-rest-functions.php 7 months ago wc-stock-functions.php 7 months ago wc-template-functions.php 4 weeks ago wc-template-hooks.php 10 months ago wc-term-functions.php 2 months ago wc-update-functions.php 5 days ago wc-user-functions.php 4 weeks ago wc-webhook-functions.php 4 weeks ago wc-widget-functions.php 5 years ago
wc-update-functions.php
3566 lines
1 <?php
2 /**
3 * WooCommerce Updates
4 *
5 * Functions for updating data, used by the background updater. These functions must be included
6 * in the list returned by WC_Install::get_db_update_callbacks.
7 *
8 * Please note that these functions are invoked when WooCommerce is updated from a previous version,
9 * but NOT when WooCommerce is newly installed.
10 *
11 * Database schema changes must be incorporated to the SQL returned by WC_Install::get_schema, which is applied
12 * via dbDelta at both install and update time. If any other kind of database change is required
13 * at install time (e.g. populating tables), use the 'woocommerce_installed' hook.
14 *
15 * @package WooCommerce\Functions
16 * @version 3.3.0
17 */
18
19 defined( 'ABSPATH' ) || exit;
20
21 use Automattic\WooCommerce\Admin\Notes\Note;
22 use Automattic\WooCommerce\Admin\Notes\Notes;
23 use Automattic\WooCommerce\Database\Migrations\MigrationHelper;
24 use Automattic\WooCommerce\Enums\DefaultCustomerAddress;
25 use Automattic\WooCommerce\Enums\ProductStockStatus;
26 use Automattic\WooCommerce\Enums\ProductType;
27 use Automattic\WooCommerce\Internal\Admin\Marketing\MarketingSpecs;
28 use Automattic\WooCommerce\Internal\Admin\Notes\WooSubscriptionsNotes;
29 use Automattic\WooCommerce\Internal\AssignDefaultCategory;
30 use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
31 use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
32 use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore;
33 use Automattic\WooCommerce\Internal\EmailEditor\WCTransactionalEmails\WCEmailTemplateSyncBackfill;
34 use Automattic\WooCommerce\Internal\Features\FeaturesController;
35 use Automattic\WooCommerce\Internal\ProductAttributesLookup\DataRegenerator;
36 use Automattic\WooCommerce\Internal\ProductAttributesLookup\LookupDataStore;
37 use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Download_Directories;
38 use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Synchronize as Download_Directories_Sync;
39 use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
40 use Automattic\WooCommerce\Utilities\StringUtil;
41 use Automattic\WooCommerce\Blocks\Options as BlockOptions;
42 use Automattic\WooCommerce\Blocks\Utils\BlockTemplateUtils;
43
44 /**
45 * Update file paths for 2.0
46 *
47 * @return void
48 */
49 function wc_update_200_file_paths() {
50 global $wpdb;
51
52 // Upgrade old style files paths to support multiple file paths.
53 $existing_file_paths = $wpdb->get_results( "SELECT meta_value, meta_id, post_id FROM {$wpdb->postmeta} WHERE meta_key = '_file_path' AND meta_value != '';" );
54
55 if ( $existing_file_paths ) {
56
57 foreach ( $existing_file_paths as $existing_file_path ) {
58
59 $old_file_path = trim( $existing_file_path->meta_value );
60
61 if ( ! empty( $old_file_path ) ) {
62 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
63 $file_paths = serialize( array( md5( $old_file_path ) => $old_file_path ) );
64
65 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_key = '_file_paths', meta_value = %s WHERE meta_id = %d", $file_paths, $existing_file_path->meta_id ) );
66
67 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}woocommerce_downloadable_product_permissions SET download_id = %s WHERE product_id = %d", md5( $old_file_path ), $existing_file_path->post_id ) );
68
69 }
70 }
71 }
72 }
73
74 /**
75 * Update permalinks for 2.0
76 *
77 * @return void
78 */
79 function wc_update_200_permalinks() {
80 // Setup default permalinks if shop page is defined.
81 $permalinks = get_option( 'woocommerce_permalinks' );
82 $shop_page_id = wc_get_page_id( 'shop' );
83
84 if ( empty( $permalinks ) && $shop_page_id > 0 ) {
85
86 $base_slug = $shop_page_id > 0 && get_post( $shop_page_id ) ? get_page_uri( $shop_page_id ) : 'shop';
87
88 $category_base = 'yes' === get_option( 'woocommerce_prepend_shop_page_to_urls' ) ? trailingslashit( $base_slug ) : '';
89 $category_slug = get_option( 'woocommerce_product_category_slug' ) ? get_option( 'woocommerce_product_category_slug' ) : _x( 'product-category', 'slug', 'woocommerce' );
90 $tag_slug = get_option( 'woocommerce_product_tag_slug' ) ? get_option( 'woocommerce_product_tag_slug' ) : _x( 'product-tag', 'slug', 'woocommerce' );
91
92 if ( 'yes' === get_option( 'woocommerce_prepend_shop_page_to_products' ) ) {
93 $product_base = trailingslashit( $base_slug );
94 } else {
95 $product_slug = get_option( 'woocommerce_product_slug' );
96 if ( false !== $product_slug && ! empty( $product_slug ) ) {
97 $product_base = trailingslashit( $product_slug );
98 } else {
99 $product_base = trailingslashit( _x( 'product', 'slug', 'woocommerce' ) );
100 }
101 }
102
103 if ( 'yes' === get_option( 'woocommerce_prepend_category_to_products' ) ) {
104 $product_base .= trailingslashit( '%product_cat%' );
105 }
106
107 $permalinks = array(
108 'product_base' => untrailingslashit( $product_base ),
109 'category_base' => untrailingslashit( $category_base . $category_slug ),
110 'attribute_base' => untrailingslashit( $category_base ),
111 'tag_base' => untrailingslashit( $category_base . $tag_slug ),
112 );
113
114 update_option( 'woocommerce_permalinks', $permalinks );
115 }
116 }
117
118 /**
119 * Update sub-category display options for 2.0
120 *
121 * @return void
122 */
123 function wc_update_200_subcat_display() {
124 // Update subcat display settings.
125 if ( 'yes' === get_option( 'woocommerce_shop_show_subcategories' ) ) {
126 if ( 'yes' === get_option( 'woocommerce_hide_products_when_showing_subcategories' ) ) {
127 update_option( 'woocommerce_shop_page_display', 'subcategories' );
128 } else {
129 update_option( 'woocommerce_shop_page_display', 'both' );
130 }
131 }
132
133 if ( 'yes' === get_option( 'woocommerce_show_subcategories' ) ) {
134 if ( 'yes' === get_option( 'woocommerce_hide_products_when_showing_subcategories' ) ) {
135 update_option( 'woocommerce_category_archive_display', 'subcategories' );
136 } else {
137 update_option( 'woocommerce_category_archive_display', 'both' );
138 }
139 }
140 }
141
142 /**
143 * Update tax rates for 2.0
144 *
145 * @return void
146 */
147 function wc_update_200_taxrates() {
148 global $wpdb;
149
150 // Update tax rates.
151 $loop = 0;
152 $tax_rates = get_option( 'woocommerce_tax_rates' );
153
154 if ( $tax_rates ) {
155 foreach ( $tax_rates as $tax_rate ) {
156
157 foreach ( $tax_rate['countries'] as $country => $states ) {
158
159 $states = array_reverse( $states );
160
161 foreach ( $states as $state ) {
162
163 if ( '*' === $state ) {
164 $state = '';
165 }
166
167 $wpdb->insert(
168 $wpdb->prefix . 'woocommerce_tax_rates',
169 array(
170 'tax_rate_country' => $country,
171 'tax_rate_state' => $state,
172 'tax_rate' => $tax_rate['rate'],
173 'tax_rate_name' => $tax_rate['label'],
174 'tax_rate_priority' => 1,
175 'tax_rate_compound' => ( 'yes' === $tax_rate['compound'] ) ? 1 : 0,
176 'tax_rate_shipping' => ( 'yes' === $tax_rate['shipping'] ) ? 1 : 0,
177 'tax_rate_order' => $loop,
178 'tax_rate_class' => $tax_rate['class'],
179 )
180 );
181
182 ++$loop;
183 }
184 }
185 }
186 }
187
188 $local_tax_rates = get_option( 'woocommerce_local_tax_rates' );
189
190 if ( $local_tax_rates ) {
191 foreach ( $local_tax_rates as $tax_rate ) {
192
193 $location_type = ( 'postcode' === $tax_rate['location_type'] ) ? 'postcode' : 'city';
194
195 if ( '*' === $tax_rate['state'] ) {
196 $tax_rate['state'] = '';
197 }
198
199 $wpdb->insert(
200 $wpdb->prefix . 'woocommerce_tax_rates',
201 array(
202 'tax_rate_country' => $tax_rate['country'],
203 'tax_rate_state' => $tax_rate['state'],
204 'tax_rate' => $tax_rate['rate'],
205 'tax_rate_name' => $tax_rate['label'],
206 'tax_rate_priority' => 2,
207 'tax_rate_compound' => ( 'yes' === $tax_rate['compound'] ) ? 1 : 0,
208 'tax_rate_shipping' => ( 'yes' === $tax_rate['shipping'] ) ? 1 : 0,
209 'tax_rate_order' => $loop,
210 'tax_rate_class' => $tax_rate['class'],
211 )
212 );
213
214 $tax_rate_id = $wpdb->insert_id;
215
216 if ( $tax_rate['locations'] ) {
217 foreach ( $tax_rate['locations'] as $location ) {
218
219 $wpdb->insert(
220 $wpdb->prefix . 'woocommerce_tax_rate_locations',
221 array(
222 'location_code' => $location,
223 'tax_rate_id' => $tax_rate_id,
224 'location_type' => $location_type,
225 )
226 );
227
228 }
229 }
230
231 ++$loop;
232 }
233 }
234
235 update_option( 'woocommerce_tax_rates_backup', $tax_rates );
236 update_option( 'woocommerce_local_tax_rates_backup', $local_tax_rates );
237 delete_option( 'woocommerce_tax_rates' );
238 delete_option( 'woocommerce_local_tax_rates' );
239 }
240
241 /**
242 * Update order item line items for 2.0
243 *
244 * @return void
245 */
246 function wc_update_200_line_items() {
247 global $wpdb;
248
249 // Now its time for the massive update to line items - move them to the new DB tables.
250 // Reverse with UPDATE `wpwc_postmeta` SET meta_key = '_order_items' WHERE meta_key = '_order_items_old'.
251 $order_item_rows = $wpdb->get_results(
252 "SELECT meta_value, post_id FROM {$wpdb->postmeta} WHERE meta_key = '_order_items'"
253 );
254
255 foreach ( $order_item_rows as $order_item_row ) {
256
257 $order_items = (array) maybe_unserialize( $order_item_row->meta_value );
258
259 foreach ( $order_items as $order_item ) {
260
261 if ( ! isset( $order_item['line_total'] ) && isset( $order_item['taxrate'] ) && isset( $order_item['cost'] ) ) {
262 $order_item['line_tax'] = number_format( ( $order_item['cost'] * $order_item['qty'] ) * ( $order_item['taxrate'] / 100 ), 2, '.', '' );
263 $order_item['line_total'] = $order_item['cost'] * $order_item['qty'];
264 $order_item['line_subtotal_tax'] = $order_item['line_tax'];
265 $order_item['line_subtotal'] = $order_item['line_total'];
266 }
267
268 $order_item['line_tax'] = isset( $order_item['line_tax'] ) ? $order_item['line_tax'] : 0;
269 $order_item['line_total'] = isset( $order_item['line_total'] ) ? $order_item['line_total'] : 0;
270 $order_item['line_subtotal_tax'] = isset( $order_item['line_subtotal_tax'] ) ? $order_item['line_subtotal_tax'] : 0;
271 $order_item['line_subtotal'] = isset( $order_item['line_subtotal'] ) ? $order_item['line_subtotal'] : 0;
272
273 $item_id = wc_add_order_item(
274 $order_item_row->post_id,
275 array(
276 'order_item_name' => $order_item['name'],
277 'order_item_type' => 'line_item',
278 )
279 );
280
281 // Add line item meta.
282 if ( $item_id ) {
283 wc_add_order_item_meta( $item_id, '_qty', absint( $order_item['qty'] ) );
284 wc_add_order_item_meta( $item_id, '_tax_class', $order_item['tax_class'] );
285 wc_add_order_item_meta( $item_id, '_product_id', $order_item['id'] );
286 wc_add_order_item_meta( $item_id, '_variation_id', $order_item['variation_id'] );
287 wc_add_order_item_meta( $item_id, '_line_subtotal', wc_format_decimal( $order_item['line_subtotal'] ) );
288 wc_add_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( $order_item['line_subtotal_tax'] ) );
289 wc_add_order_item_meta( $item_id, '_line_total', wc_format_decimal( $order_item['line_total'] ) );
290 wc_add_order_item_meta( $item_id, '_line_tax', wc_format_decimal( $order_item['line_tax'] ) );
291
292 $meta_rows = array();
293
294 // Insert meta.
295 if ( ! empty( $order_item['item_meta'] ) ) {
296 foreach ( $order_item['item_meta'] as $key => $meta ) {
297 // Backwards compatibility.
298 if ( is_array( $meta ) && isset( $meta['meta_name'] ) ) {
299 $meta_rows[] = '(' . $item_id . ',"' . esc_sql( $meta['meta_name'] ) . '","' . esc_sql( $meta['meta_value'] ) . '")';
300 } else {
301 $meta_rows[] = '(' . $item_id . ',"' . esc_sql( $key ) . '","' . esc_sql( $meta ) . '")';
302 }
303 }
304 }
305
306 // Insert meta rows at once.
307 if ( count( $meta_rows ) > 0 ) {
308 $wpdb->query(
309 $wpdb->prepare(
310 "INSERT INTO {$wpdb->prefix}woocommerce_order_itemmeta ( order_item_id, meta_key, meta_value )
311 VALUES " . implode( ',', $meta_rows ) . ';', // @codingStandardsIgnoreLine
312 $order_item_row->post_id
313 )
314 );
315 }
316
317 // Delete from DB (rename).
318 $wpdb->query(
319 $wpdb->prepare(
320 "UPDATE {$wpdb->postmeta}
321 SET meta_key = '_order_items_old'
322 WHERE meta_key = '_order_items'
323 AND post_id = %d",
324 $order_item_row->post_id
325 )
326 );
327 }
328
329 unset( $meta_rows, $item_id, $order_item );
330 }
331 }
332
333 // Do the same kind of update for order_taxes - move to lines.
334 // Reverse with UPDATE `wpwc_postmeta` SET meta_key = '_order_taxes' WHERE meta_key = '_order_taxes_old'.
335 $order_tax_rows = $wpdb->get_results(
336 "SELECT meta_value, post_id FROM {$wpdb->postmeta}
337 WHERE meta_key = '_order_taxes'"
338 );
339
340 foreach ( $order_tax_rows as $order_tax_row ) {
341
342 $order_taxes = (array) maybe_unserialize( $order_tax_row->meta_value );
343
344 if ( ! empty( $order_taxes ) ) {
345 foreach ( $order_taxes as $order_tax ) {
346
347 if ( ! isset( $order_tax['label'] ) || ! isset( $order_tax['cart_tax'] ) || ! isset( $order_tax['shipping_tax'] ) ) {
348 continue;
349 }
350
351 $item_id = wc_add_order_item(
352 $order_tax_row->post_id,
353 array(
354 'order_item_name' => $order_tax['label'],
355 'order_item_type' => 'tax',
356 )
357 );
358
359 // Add line item meta.
360 if ( $item_id ) {
361 wc_add_order_item_meta( $item_id, 'compound', absint( isset( $order_tax['compound'] ) ? $order_tax['compound'] : 0 ) );
362 wc_add_order_item_meta( $item_id, 'tax_amount', wc_clean( $order_tax['cart_tax'] ) );
363 wc_add_order_item_meta( $item_id, 'shipping_tax_amount', wc_clean( $order_tax['shipping_tax'] ) );
364 }
365
366 // Delete from DB (rename).
367 $wpdb->query(
368 $wpdb->prepare(
369 "UPDATE {$wpdb->postmeta}
370 SET meta_key = '_order_taxes_old'
371 WHERE meta_key = '_order_taxes'
372 AND post_id = %d",
373 $order_tax_row->post_id
374 )
375 );
376 }
377 }
378 }
379 }
380
381 /**
382 * Update image settings for 2.0
383 *
384 * @return void
385 */
386 function wc_update_200_images() {
387 // Grab the pre 2.0 Image options and use to populate the new image options settings,
388 // cleaning up afterwards like nice people do.
389 foreach ( array( 'catalog', 'single', 'thumbnail' ) as $value ) {
390
391 $old_settings = array_filter(
392 array(
393 'width' => get_option( 'woocommerce_' . $value . '_image_width' ),
394 'height' => get_option( 'woocommerce_' . $value . '_image_height' ),
395 'crop' => get_option( 'woocommerce_' . $value . '_image_crop' ),
396 )
397 );
398
399 if ( ! empty( $old_settings ) && update_option( 'shop_' . $value . '_image_size', $old_settings ) ) {
400
401 delete_option( 'woocommerce_' . $value . '_image_width' );
402 delete_option( 'woocommerce_' . $value . '_image_height' );
403 delete_option( 'woocommerce_' . $value . '_image_crop' );
404
405 }
406 }
407 }
408
409 /**
410 * Update DB version for 2.0
411 *
412 * @return void
413 */
414 function wc_update_200_db_version() {
415 WC_Install::update_db_version( '2.0.0' );
416 }
417
418 /**
419 * Update Brazilian States for 2.0.9
420 *
421 * @return void
422 */
423 function wc_update_209_brazillian_state() {
424 global $wpdb;
425
426 // phpcs:disable WordPress.DB.SlowDBQuery
427
428 // Update Brazilian state codes.
429 $wpdb->update(
430 $wpdb->postmeta,
431 array(
432 'meta_value' => 'BA',
433 ),
434 array(
435 'meta_key' => '_billing_state',
436 'meta_value' => 'BH',
437 )
438 );
439 $wpdb->update(
440 $wpdb->postmeta,
441 array(
442 'meta_value' => 'BA',
443 ),
444 array(
445 'meta_key' => '_shipping_state',
446 'meta_value' => 'BH',
447 )
448 );
449 $wpdb->update(
450 $wpdb->usermeta,
451 array(
452 'meta_value' => 'BA',
453 ),
454 array(
455 'meta_key' => 'billing_state',
456 'meta_value' => 'BH',
457 )
458 );
459 $wpdb->update(
460 $wpdb->usermeta,
461 array(
462 'meta_value' => 'BA',
463 ),
464 array(
465 'meta_key' => 'shipping_state',
466 'meta_value' => 'BH',
467 )
468 );
469
470 // phpcs:enable WordPress.DB.SlowDBQuery
471 }
472
473 /**
474 * Update DB version for 2.0.9
475 *
476 * @return void
477 */
478 function wc_update_209_db_version() {
479 WC_Install::update_db_version( '2.0.9' );
480 }
481
482 /**
483 * Remove pages for 2.1
484 *
485 * @return void
486 */
487 function wc_update_210_remove_pages() {
488 // Pages no longer used.
489 wp_trash_post( get_option( 'woocommerce_pay_page_id' ) );
490 wp_trash_post( get_option( 'woocommerce_thanks_page_id' ) );
491 wp_trash_post( get_option( 'woocommerce_view_order_page_id' ) );
492 wp_trash_post( get_option( 'woocommerce_change_password_page_id' ) );
493 wp_trash_post( get_option( 'woocommerce_edit_address_page_id' ) );
494 wp_trash_post( get_option( 'woocommerce_lost_password_page_id' ) );
495 }
496
497 /**
498 * Update file paths to support multiple files for 2.1
499 *
500 * @return void
501 */
502 function wc_update_210_file_paths() {
503 global $wpdb;
504
505 // Upgrade file paths to support multiple file paths + names etc.
506 $existing_file_paths = $wpdb->get_results( "SELECT meta_value, meta_id FROM {$wpdb->postmeta} WHERE meta_key = '_file_paths' AND meta_value != '';" );
507
508 if ( $existing_file_paths ) {
509
510 foreach ( $existing_file_paths as $existing_file_path ) {
511
512 $needs_update = false;
513 $new_value = array();
514 $value = maybe_unserialize( trim( $existing_file_path->meta_value ) );
515
516 if ( $value ) {
517 foreach ( $value as $key => $file ) {
518 if ( ! is_array( $file ) ) {
519 $needs_update = true;
520 $new_value[ $key ] = array(
521 'file' => $file,
522 'name' => wc_get_filename_from_url( $file ),
523 );
524 } else {
525 $new_value[ $key ] = $file;
526 }
527 }
528 if ( $needs_update ) {
529 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
530 $new_value = serialize( $new_value );
531
532 $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_key = %s, meta_value = %s WHERE meta_id = %d", '_downloadable_files', $new_value, $existing_file_path->meta_id ) );
533 }
534 }
535 }
536 }
537 }
538
539 /**
540 * Update DB version for 2.1
541 *
542 * @return void
543 */
544 function wc_update_210_db_version() {
545 WC_Install::update_db_version( '2.1.0' );
546 }
547
548 /**
549 * Update shipping options for 2.2
550 *
551 * @return void
552 */
553 function wc_update_220_shipping() {
554 $woocommerce_ship_to_destination = 'shipping';
555
556 if ( get_option( 'woocommerce_ship_to_billing_address_only' ) === 'yes' ) {
557 $woocommerce_ship_to_destination = 'billing_only';
558 } elseif ( get_option( 'woocommerce_ship_to_billing' ) === 'yes' ) {
559 $woocommerce_ship_to_destination = 'billing';
560 }
561
562 add_option( 'woocommerce_ship_to_destination', $woocommerce_ship_to_destination, '', 'no' );
563 }
564
565 /**
566 * Update order statuses for 2.2
567 *
568 * Keeping the internal statuses names as strings to avoid regression issues (not referencing Automattic\WooCommerce\Enums\OrderInternalStatus class).
569 *
570 * @return void
571 */
572 function wc_update_220_order_status() {
573 global $wpdb;
574 $wpdb->query(
575 "UPDATE {$wpdb->posts} as posts
576 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_id
577 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
578 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
579 SET posts.post_status = 'wc-pending'
580 WHERE posts.post_type = 'shop_order'
581 AND posts.post_status = 'publish'
582 AND tax.taxonomy = 'shop_order_status'
583 AND term.slug LIKE 'pending%';"
584 );
585 $wpdb->query(
586 "UPDATE {$wpdb->posts} as posts
587 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_id
588 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
589 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
590 SET posts.post_status = 'wc-processing'
591 WHERE posts.post_type = 'shop_order'
592 AND posts.post_status = 'publish'
593 AND tax.taxonomy = 'shop_order_status'
594 AND term.slug LIKE 'processing%';"
595 );
596 $wpdb->query(
597 "UPDATE {$wpdb->posts} as posts
598 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_id
599 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
600 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
601 SET posts.post_status = 'wc-on-hold'
602 WHERE posts.post_type = 'shop_order'
603 AND posts.post_status = 'publish'
604 AND tax.taxonomy = 'shop_order_status'
605 AND term.slug LIKE 'on-hold%';"
606 );
607 $wpdb->query(
608 "UPDATE {$wpdb->posts} as posts
609 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_id
610 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
611 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
612 SET posts.post_status = 'wc-completed'
613 WHERE posts.post_type = 'shop_order'
614 AND posts.post_status = 'publish'
615 AND tax.taxonomy = 'shop_order_status'
616 AND term.slug LIKE 'completed%';"
617 );
618 $wpdb->query(
619 "UPDATE {$wpdb->posts} as posts
620 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_id
621 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
622 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
623 SET posts.post_status = 'wc-cancelled'
624 WHERE posts.post_type = 'shop_order'
625 AND posts.post_status = 'publish'
626 AND tax.taxonomy = 'shop_order_status'
627 AND term.slug LIKE 'cancelled%';"
628 );
629 $wpdb->query(
630 "UPDATE {$wpdb->posts} as posts
631 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_id
632 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
633 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
634 SET posts.post_status = 'wc-refunded'
635 WHERE posts.post_type = 'shop_order'
636 AND posts.post_status = 'publish'
637 AND tax.taxonomy = 'shop_order_status'
638 AND term.slug LIKE 'refunded%';"
639 );
640 $wpdb->query(
641 "UPDATE {$wpdb->posts} as posts
642 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID = rel.object_id
643 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
644 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
645 SET posts.post_status = 'wc-failed'
646 WHERE posts.post_type = 'shop_order'
647 AND posts.post_status = 'publish'
648 AND tax.taxonomy = 'shop_order_status'
649 AND term.slug LIKE 'failed%';"
650 );
651 }
652
653 /**
654 * Update variations for 2.2
655 *
656 * @return void
657 */
658 function wc_update_220_variations() {
659 global $wpdb;
660 // Update variations which manage stock.
661 $update_variations = $wpdb->get_results(
662 "SELECT DISTINCT posts.ID AS variation_id, posts.post_parent AS variation_parent FROM {$wpdb->posts} as posts
663 LEFT OUTER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id AND postmeta.meta_key = '_stock'
664 LEFT OUTER JOIN {$wpdb->postmeta} as postmeta2 ON posts.ID = postmeta2.post_id AND postmeta2.meta_key = '_manage_stock'
665 WHERE posts.post_type = 'product_variation'
666 AND postmeta.meta_value IS NOT NULL
667 AND postmeta.meta_value != ''
668 AND postmeta2.meta_value IS NULL"
669 );
670
671 foreach ( $update_variations as $variation ) {
672 $parent_backorders = get_post_meta( $variation->variation_parent, '_backorders', true );
673 add_post_meta( $variation->variation_id, '_manage_stock', 'yes', true );
674 add_post_meta( $variation->variation_id, '_backorders', $parent_backorders ? $parent_backorders : 'no', true );
675 }
676 }
677
678 /**
679 * Update attributes for 2.2
680 *
681 * @return void
682 */
683 function wc_update_220_attributes() {
684 global $wpdb;
685 // Update taxonomy names with correct sanitized names.
686 $attribute_taxonomies = $wpdb->get_results( 'SELECT attribute_name, attribute_id FROM ' . $wpdb->prefix . 'woocommerce_attribute_taxonomies' );
687
688 foreach ( $attribute_taxonomies as $attribute_taxonomy ) {
689 $sanitized_attribute_name = wc_sanitize_taxonomy_name( $attribute_taxonomy->attribute_name );
690 if ( $sanitized_attribute_name !== $attribute_taxonomy->attribute_name ) {
691 if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT 1=1 FROM {$wpdb->prefix}woocommerce_attribute_taxonomies WHERE attribute_name = %s;", $sanitized_attribute_name ) ) ) {
692 // Update attribute.
693 $wpdb->update(
694 "{$wpdb->prefix}woocommerce_attribute_taxonomies",
695 array(
696 'attribute_name' => $sanitized_attribute_name,
697 ),
698 array(
699 'attribute_id' => $attribute_taxonomy->attribute_id,
700 )
701 );
702
703 // Update terms.
704 $wpdb->update(
705 $wpdb->term_taxonomy,
706 array( 'taxonomy' => wc_attribute_taxonomy_name( $sanitized_attribute_name ) ),
707 array( 'taxonomy' => 'pa_' . $attribute_taxonomy->attribute_name )
708 );
709 }
710 }
711 }
712
713 delete_transient( 'wc_attribute_taxonomies' );
714 WC_Cache_Helper::invalidate_cache_group( 'woocommerce-attributes' );
715 }
716
717 /**
718 * Update DB version for 2.2
719 *
720 * @return void
721 */
722 function wc_update_220_db_version() {
723 WC_Install::update_db_version( '2.2.0' );
724 }
725
726 /**
727 * Update options for 2.3
728 *
729 * @return void
730 */
731 function wc_update_230_options() {
732 // _money_spent and _order_count may be out of sync - clear them
733 delete_metadata( 'user', 0, '_money_spent', '', true );
734 delete_metadata( 'user', 0, '_order_count', '', true );
735 delete_metadata( 'user', 0, '_last_order', '', true );
736
737 // To prevent taxes being hidden when using a default 'no address' in a store with tax inc prices, set the woocommerce_default_customer_address to use the store base address by default.
738 if ( DefaultCustomerAddress::NO_DEFAULT === get_option( 'woocommerce_default_customer_address', false ) && wc_prices_include_tax() ) {
739 update_option( 'woocommerce_default_customer_address', DefaultCustomerAddress::BASE );
740 }
741 }
742
743 /**
744 * Update DB version for 2.3
745 *
746 * @return void
747 */
748 function wc_update_230_db_version() {
749 WC_Install::update_db_version( '2.3.0' );
750 }
751
752 /**
753 * Update calc discount options for 2.4
754 *
755 * @return void
756 */
757 function wc_update_240_options() {
758 /**
759 * Coupon discount calculations.
760 * Maintain the old coupon logic for upgrades.
761 */
762 update_option( 'woocommerce_calc_discounts_sequentially', 'yes' );
763 }
764
765 /**
766 * Update shipping methods for 2.4
767 *
768 * @return void
769 */
770 function wc_update_240_shipping_methods() {
771 /**
772 * Flat Rate Shipping.
773 * Update legacy options to new math based options.
774 */
775 $shipping_methods = array(
776 'woocommerce_flat_rates' => new WC_Shipping_Legacy_Flat_Rate(),
777 'woocommerce_international_delivery_flat_rates' => new WC_Shipping_Legacy_International_Delivery(),
778 );
779 foreach ( $shipping_methods as $flat_rate_option_key => $shipping_method ) {
780 // Stop this running more than once if routine is repeated.
781 if ( version_compare( $shipping_method->get_option( 'version', 0 ), '2.4.0', '<' ) ) {
782 $shipping_classes = WC()->shipping()->get_shipping_classes();
783 $has_classes = count( $shipping_classes ) > 0;
784 $cost_key = $has_classes ? 'no_class_cost' : 'cost';
785 $min_fee = $shipping_method->get_option( 'minimum_fee' );
786 $math_cost_strings = array(
787 'cost' => array(),
788 'no_class_cost' => array(),
789 );
790
791 $math_cost_strings[ $cost_key ][] = $shipping_method->get_option( 'cost' );
792 $fee = $shipping_method->get_option( 'fee' );
793
794 if ( $fee ) {
795 $math_cost_strings[ $cost_key ][] = strstr( $fee, '%' ) ? '[fee percent="' . str_replace( '%', '', $fee ) . '" min="' . esc_attr( $min_fee ) . '"]' : $fee;
796 }
797
798 foreach ( $shipping_classes as $shipping_class ) {
799 $rate_key = 'class_cost_' . $shipping_class->slug;
800 $math_cost_strings[ $rate_key ] = $math_cost_strings['no_class_cost'];
801 }
802
803 $flat_rates = array_filter( (array) get_option( $flat_rate_option_key, array() ) );
804
805 if ( $flat_rates ) {
806 foreach ( $flat_rates as $shipping_class => $rate ) {
807 $rate_key = 'class_cost_' . $shipping_class;
808 if ( $rate['cost'] || $rate['fee'] ) {
809 $math_cost_strings[ $rate_key ][] = $rate['cost'];
810 $math_cost_strings[ $rate_key ][] = strstr( $rate['fee'], '%' ) ? '[fee percent="' . str_replace( '%', '', $rate['fee'] ) . '" min="' . esc_attr( $min_fee ) . '"]' : $rate['fee'];
811 }
812 }
813 }
814
815 if ( 'item' === $shipping_method->type ) {
816 foreach ( $math_cost_strings as $key => $math_cost_string ) {
817 $math_cost_strings[ $key ] = array_filter( array_map( 'trim', $math_cost_strings[ $key ] ) );
818 if ( ! empty( $math_cost_strings[ $key ] ) ) {
819 $last_key = max( 0, count( $math_cost_strings[ $key ] ) - 1 );
820 $math_cost_strings[ $key ][0] = '( ' . $math_cost_strings[ $key ][0];
821 $math_cost_strings[ $key ][ $last_key ] .= ' ) * [qty]';
822 }
823 }
824 }
825
826 $math_cost_strings['cost'][] = $shipping_method->get_option( 'cost_per_order' );
827
828 // Save settings.
829 foreach ( $math_cost_strings as $option_id => $math_cost_string ) {
830 $shipping_method->settings[ $option_id ] = implode( ' + ', array_filter( $math_cost_string ) );
831 }
832
833 $shipping_method->settings['version'] = '2.4.0';
834 $shipping_method->settings['type'] = 'item' === $shipping_method->settings['type'] ? 'class' : $shipping_method->settings['type'];
835
836 update_option( $shipping_method->plugin_id . $shipping_method->id . '_settings', $shipping_method->settings );
837 }
838 }
839 }
840
841 /**
842 * Update API keys for 2.4
843 *
844 * @return void
845 */
846 function wc_update_240_api_keys() {
847 global $wpdb;
848 /**
849 * Update the old user API keys to the new Apps keys.
850 */
851 $api_users = $wpdb->get_results( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = 'woocommerce_api_consumer_key'" );
852 $apps_keys = array();
853
854 // Get user data.
855 foreach ( $api_users as $_user ) {
856 $user = get_userdata( $_user->user_id );
857 $apps_keys[] = array(
858 'user_id' => $user->ID,
859 'permissions' => $user->woocommerce_api_key_permissions,
860 'consumer_key' => wc_api_hash( $user->woocommerce_api_consumer_key ),
861 'consumer_secret' => $user->woocommerce_api_consumer_secret,
862 'truncated_key' => substr( $user->woocommerce_api_consumer_secret, -7 ),
863 );
864 }
865
866 if ( ! empty( $apps_keys ) ) {
867 // Create new apps.
868 foreach ( $apps_keys as $app ) {
869 $wpdb->insert(
870 $wpdb->prefix . 'woocommerce_api_keys',
871 $app,
872 array(
873 '%d',
874 '%s',
875 '%s',
876 '%s',
877 '%s',
878 )
879 );
880 }
881
882 // Delete old user keys from usermeta.
883 foreach ( $api_users as $_user ) {
884 $user_id = intval( $_user->user_id );
885 delete_user_meta( $user_id, 'woocommerce_api_consumer_key' );
886 delete_user_meta( $user_id, 'woocommerce_api_consumer_secret' );
887 delete_user_meta( $user_id, 'woocommerce_api_key_permissions' );
888 }
889 }
890 }
891
892 /**
893 * Update webhooks for 2.4
894 *
895 * @return void
896 */
897 function wc_update_240_webhooks() {
898 // phpcs:disable WordPress.DB.SlowDBQuery
899
900 /**
901 * Webhooks.
902 * Make sure order.update webhooks get the woocommerce_order_edit_status hook.
903 */
904 $order_update_webhooks = get_posts(
905 array(
906 'posts_per_page' => -1,
907 'post_type' => 'shop_webhook',
908 'meta_key' => '_topic',
909 'meta_value' => 'order.updated',
910 )
911 );
912 foreach ( $order_update_webhooks as $order_update_webhook ) {
913 $webhook = new WC_Webhook( $order_update_webhook->ID );
914 $webhook->set_topic( 'order.updated' );
915 }
916
917 // phpcs:enable WordPress.DB.SlowDBQuery
918 }
919
920 /**
921 * Update refunds for 2.4
922 *
923 * @return void
924 */
925 function wc_update_240_refunds() {
926 global $wpdb;
927 /**
928 * Refunds for full refunded orders.
929 * Update fully refunded orders to ensure they have a refund line item so reports add up.
930 */
931 $refunded_orders = get_posts(
932 array(
933 'posts_per_page' => -1,
934 'post_type' => 'shop_order',
935 'post_status' => array( 'wc-refunded' ),
936 )
937 );
938
939 // Ensure emails are disabled during this update routine.
940 remove_all_actions( 'woocommerce_order_status_refunded_notification' );
941 remove_all_actions( 'woocommerce_order_partially_refunded_notification' );
942 remove_action( 'woocommerce_order_status_refunded', array( 'WC_Emails', 'send_transactional_email' ) );
943 remove_action( 'woocommerce_order_partially_refunded', array( 'WC_Emails', 'send_transactional_email' ) );
944
945 foreach ( $refunded_orders as $refunded_order ) {
946 $order_total = get_post_meta( $refunded_order->ID, '_order_total', true );
947 $refunded_total = $wpdb->get_var(
948 $wpdb->prepare(
949 "SELECT SUM( postmeta.meta_value )
950 FROM $wpdb->postmeta AS postmeta
951 INNER JOIN $wpdb->posts AS posts ON ( posts.post_type = 'shop_order_refund' AND posts.post_parent = %d )
952 WHERE postmeta.meta_key = '_refund_amount'
953 AND postmeta.post_id = posts.ID",
954 $refunded_order->ID
955 )
956 );
957
958 if ( $order_total > $refunded_total ) {
959 wc_create_refund(
960 array(
961 'amount' => $order_total - $refunded_total,
962 'reason' => __( 'Order fully refunded', 'woocommerce' ),
963 'order_id' => $refunded_order->ID,
964 'line_items' => array(),
965 'date' => $refunded_order->post_modified,
966 )
967 );
968 }
969 }
970
971 wc_delete_shop_order_transients();
972 }
973
974 /**
975 * Update DB version for 2.4
976 *
977 * @return void
978 */
979 function wc_update_240_db_version() {
980 WC_Install::update_db_version( '2.4.0' );
981 }
982
983 /**
984 * Update variations for 2.4.1
985 *
986 * @return void
987 */
988 function wc_update_241_variations() {
989 global $wpdb;
990
991 // Select variations that don't have any _stock_status implemented on WooCommerce 2.2.
992 $update_variations = $wpdb->get_results(
993 "SELECT DISTINCT posts.ID AS variation_id, posts.post_parent AS variation_parent
994 FROM {$wpdb->posts} as posts
995 LEFT OUTER JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id AND postmeta.meta_key = '_stock_status'
996 WHERE posts.post_type = 'product_variation'
997 AND postmeta.meta_value IS NULL"
998 );
999
1000 foreach ( $update_variations as $variation ) {
1001 // Get the parent _stock_status.
1002 $parent_stock_status = get_post_meta( $variation->variation_parent, '_stock_status', true );
1003
1004 // Set the _stock_status.
1005 add_post_meta( $variation->variation_id, '_stock_status', $parent_stock_status ? $parent_stock_status : ProductStockStatus::IN_STOCK, true );
1006
1007 // Delete old product children array.
1008 delete_transient( 'wc_product_children_' . $variation->variation_parent );
1009 }
1010
1011 // Invalidate old transients such as wc_var_price.
1012 WC_Cache_Helper::get_transient_version( 'product', true );
1013 }
1014
1015 /**
1016 * Update DB version for 2.4.1
1017 *
1018 * @return void
1019 */
1020 function wc_update_241_db_version() {
1021 WC_Install::update_db_version( '2.4.1' );
1022 }
1023
1024 /**
1025 * Update currency settings for 2.5
1026 *
1027 * @return void
1028 */
1029 function wc_update_250_currency() {
1030 global $wpdb;
1031 // Fix currency settings for LAK currency.
1032 $current_currency = get_option( 'woocommerce_currency' );
1033
1034 if ( 'KIP' === $current_currency ) {
1035 update_option( 'woocommerce_currency', 'LAK' );
1036 }
1037
1038 // phpcs:disable WordPress.DB.SlowDBQuery
1039
1040 // Update LAK currency code.
1041 $wpdb->update(
1042 $wpdb->postmeta,
1043 array(
1044 'meta_value' => 'LAK',
1045 ),
1046 array(
1047 'meta_key' => '_order_currency',
1048 'meta_value' => 'KIP',
1049 )
1050 );
1051
1052 // phpcs:enable WordPress.DB.SlowDBQuery
1053 }
1054
1055 /**
1056 * Update DB version for 2.5
1057 *
1058 * @return void
1059 */
1060 function wc_update_250_db_version() {
1061 WC_Install::update_db_version( '2.5.0' );
1062 }
1063
1064 /**
1065 * Update ship to countries options for 2.6
1066 *
1067 * @return void
1068 */
1069 function wc_update_260_options() {
1070 // woocommerce_calc_shipping option has been removed in 2.6.
1071 if ( 'no' === get_option( 'woocommerce_calc_shipping' ) ) {
1072 update_option( 'woocommerce_ship_to_countries', 'disabled' );
1073 }
1074
1075 WC_Admin_Notices::add_notice( 'legacy_shipping' );
1076 }
1077
1078 /**
1079 * Update term meta for 2.6
1080 *
1081 * @return void
1082 */
1083 function wc_update_260_termmeta() {
1084 global $wpdb;
1085 /**
1086 * Migrate term meta to WordPress tables.
1087 */
1088 if ( get_option( 'db_version' ) >= 34370 && $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_termmeta';" ) ) {
1089 if ( $wpdb->query( "INSERT INTO {$wpdb->termmeta} ( term_id, meta_key, meta_value ) SELECT woocommerce_term_id, meta_key, meta_value FROM {$wpdb->prefix}woocommerce_termmeta;" ) ) {
1090 $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_termmeta" );
1091 wp_cache_flush();
1092 }
1093 }
1094 }
1095
1096 /**
1097 * Update zones for 2.6
1098 *
1099 * @return void
1100 */
1101 function wc_update_260_zones() {
1102 global $wpdb;
1103 /**
1104 * Old (table rate) shipping zones to new core shipping zones migration.
1105 * zone_enabled and zone_type are no longer used, but it's safe to leave them be.
1106 */
1107 if ( $wpdb->get_var( "SHOW COLUMNS FROM `{$wpdb->prefix}woocommerce_shipping_zones` LIKE 'zone_enabled';" ) ) {
1108 $wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_shipping_zones CHANGE `zone_type` `zone_type` VARCHAR(40) NOT NULL DEFAULT '';" );
1109 $wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_shipping_zones CHANGE `zone_enabled` `zone_enabled` INT(1) NOT NULL DEFAULT 1;" );
1110 }
1111 }
1112
1113 /**
1114 * Update zone methods for 2.6
1115 *
1116 * @return void
1117 */
1118 function wc_update_260_zone_methods() {
1119 global $wpdb;
1120
1121 /**
1122 * Shipping zones in WC 2.6.0 use a table named woocommerce_shipping_zone_methods.
1123 * Migrate the old data out of woocommerce_shipping_zone_shipping_methods into the new table and port over any known options (used by table rates and flat rate boxes).
1124 */
1125 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_shipping_zone_shipping_methods';" ) ) {
1126 $old_methods = $wpdb->get_results( "SELECT zone_id, shipping_method_type, shipping_method_order, shipping_method_id FROM {$wpdb->prefix}woocommerce_shipping_zone_shipping_methods;" );
1127
1128 if ( $old_methods ) {
1129 $max_new_id = $wpdb->get_var( "SELECT MAX(instance_id) FROM {$wpdb->prefix}woocommerce_shipping_zone_methods" );
1130 $max_old_id = $wpdb->get_var( "SELECT MAX(shipping_method_id) FROM {$wpdb->prefix}woocommerce_shipping_zone_shipping_methods" );
1131
1132 // Avoid ID conflicts.
1133 $wpdb->query( $wpdb->prepare( "ALTER TABLE {$wpdb->prefix}woocommerce_shipping_zone_methods AUTO_INCREMENT = %d;", max( $max_new_id, $max_old_id ) + 1 ) );
1134
1135 // Store changes.
1136 $changes = array();
1137
1138 // Move data.
1139 foreach ( $old_methods as $old_method ) {
1140 $wpdb->insert(
1141 $wpdb->prefix . 'woocommerce_shipping_zone_methods',
1142 array(
1143 'zone_id' => $old_method->zone_id,
1144 'method_id' => $old_method->shipping_method_type,
1145 'method_order' => $old_method->shipping_method_order,
1146 )
1147 );
1148
1149 $new_instance_id = $wpdb->insert_id;
1150
1151 // Move main settings.
1152 $older_settings_key = 'woocommerce_' . $old_method->shipping_method_type . '-' . $old_method->shipping_method_id . '_settings';
1153 $old_settings_key = 'woocommerce_' . $old_method->shipping_method_type . '_' . $old_method->shipping_method_id . '_settings';
1154 add_option( 'woocommerce_' . $old_method->shipping_method_type . '_' . $new_instance_id . '_settings', get_option( $old_settings_key, get_option( $older_settings_key ) ) );
1155
1156 // Handling for table rate and flat rate box shipping.
1157 if ( 'table_rate' === $old_method->shipping_method_type ) {
1158 // Move priority settings.
1159 add_option( 'woocommerce_table_rate_default_priority_' . $new_instance_id, get_option( 'woocommerce_table_rate_default_priority_' . $old_method->shipping_method_id ) );
1160 add_option( 'woocommerce_table_rate_priorities_' . $new_instance_id, get_option( 'woocommerce_table_rate_priorities_' . $old_method->shipping_method_id ) );
1161
1162 // Move rates.
1163 $wpdb->update(
1164 $wpdb->prefix . 'woocommerce_shipping_table_rates',
1165 array(
1166 'shipping_method_id' => $new_instance_id,
1167 ),
1168 array(
1169 'shipping_method_id' => $old_method->shipping_method_id,
1170 )
1171 );
1172 } elseif ( 'flat_rate_boxes' === $old_method->shipping_method_type ) {
1173 $wpdb->update(
1174 $wpdb->prefix . 'woocommerce_shipping_flat_rate_boxes',
1175 array(
1176 'shipping_method_id' => $new_instance_id,
1177 ),
1178 array(
1179 'shipping_method_id' => $old_method->shipping_method_id,
1180 )
1181 );
1182 }
1183
1184 $changes[ $old_method->shipping_method_id ] = $new_instance_id;
1185 }
1186
1187 // $changes contains keys (old method ids) and values (new instance ids) if extra processing is needed in plugins.
1188 // Store this to an option so extensions can pick it up later, then fire an action.
1189 update_option( 'woocommerce_updated_instance_ids', $changes );
1190 do_action( 'woocommerce_updated_instance_ids', $changes );
1191 }
1192 }
1193
1194 // Change ranges used to ...
1195 $wpdb->query( "UPDATE {$wpdb->prefix}woocommerce_shipping_zone_locations SET location_code = REPLACE( location_code, '-', '...' );" );
1196 }
1197
1198 /**
1199 * Update refunds for 2.6
1200 *
1201 * @return void
1202 */
1203 function wc_update_260_refunds() {
1204 global $wpdb;
1205 /**
1206 * Refund item qty should be negative.
1207 */
1208 $wpdb->query(
1209 "UPDATE {$wpdb->prefix}woocommerce_order_itemmeta as item_meta
1210 LEFT JOIN {$wpdb->prefix}woocommerce_order_items as items ON item_meta.order_item_id = items.order_item_id
1211 LEFT JOIN {$wpdb->posts} as posts ON items.order_id = posts.ID
1212 SET item_meta.meta_value = item_meta.meta_value * -1
1213 WHERE item_meta.meta_value > 0 AND item_meta.meta_key = '_qty' AND posts.post_type = 'shop_order_refund'"
1214 );
1215 }
1216
1217 /**
1218 * Update DB version for 2.6
1219 *
1220 * @return void
1221 */
1222 function wc_update_260_db_version() {
1223 WC_Install::update_db_version( '2.6.0' );
1224 }
1225
1226 /**
1227 * Update webhooks for 3.0
1228 *
1229 * @return void
1230 */
1231 function wc_update_300_webhooks() {
1232 // phpcs:disable WordPress.DB.SlowDBQuery
1233
1234 /**
1235 * Make sure product.update webhooks get the woocommerce_product_quick_edit_save
1236 * and woocommerce_product_bulk_edit_save hooks.
1237 */
1238 $product_update_webhooks = get_posts(
1239 array(
1240 'posts_per_page' => -1,
1241 'post_type' => 'shop_webhook',
1242 'meta_key' => '_topic',
1243 'meta_value' => 'product.updated',
1244 )
1245 );
1246 foreach ( $product_update_webhooks as $product_update_webhook ) {
1247 $webhook = new WC_Webhook( $product_update_webhook->ID );
1248 $webhook->set_topic( 'product.updated' );
1249 }
1250
1251 // phpcs:enable WordPress.DB.SlowDBQuery
1252 }
1253
1254 /**
1255 * Add an index to the field comment_type to improve the response time of the query
1256 * used by WC_Comments::wp_count_comments() to get the number of comments by type.
1257 *
1258 * @return void
1259 */
1260 function wc_update_300_comment_type_index() {
1261 global $wpdb;
1262
1263 $index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE column_name = 'comment_type' and key_name = 'woo_idx_comment_type'" );
1264
1265 if ( is_null( $index_exists ) ) {
1266 // Add an index to the field comment_type to improve the response time of the query
1267 // used by WC_Comments::wp_count_comments() to get the number of comments by type.
1268 $wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_type (comment_type)" );
1269 }
1270 }
1271
1272 /**
1273 * Update grouped products for 3.0
1274 *
1275 * @return void
1276 */
1277 function wc_update_300_grouped_products() {
1278 global $wpdb;
1279 $parents = $wpdb->get_col( "SELECT DISTINCT( post_parent ) FROM {$wpdb->posts} WHERE post_parent > 0 AND post_type = 'product';" );
1280 foreach ( $parents as $parent_id ) {
1281 $parent = wc_get_product( $parent_id );
1282 if ( $parent && $parent->is_type( ProductType::GROUPED ) ) {
1283 $children_ids = get_posts(
1284 array(
1285 'post_parent' => $parent_id,
1286 'posts_per_page' => -1,
1287 'post_type' => 'product',
1288 'fields' => 'ids',
1289 )
1290 );
1291 update_post_meta( $parent_id, '_children', $children_ids );
1292
1293 // Update children to remove the parent.
1294 $wpdb->update(
1295 $wpdb->posts,
1296 array(
1297 'post_parent' => 0,
1298 ),
1299 array(
1300 'post_parent' => $parent_id,
1301 )
1302 );
1303 }
1304 }
1305 }
1306
1307 /**
1308 * Update shipping tax classes for 3.0
1309 *
1310 * @return void
1311 */
1312 function wc_update_300_settings() {
1313 $woocommerce_shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' );
1314 if ( '' === $woocommerce_shipping_tax_class ) {
1315 update_option( 'woocommerce_shipping_tax_class', 'inherit' );
1316 } elseif ( 'standard' === $woocommerce_shipping_tax_class ) {
1317 update_option( 'woocommerce_shipping_tax_class', '' );
1318 }
1319 }
1320
1321 /**
1322 * Convert meta values into term for product visibility.
1323 *
1324 * @return void
1325 */
1326 function wc_update_300_product_visibility() {
1327 global $wpdb;
1328
1329 WC_Install::create_terms();
1330
1331 $featured_term = get_term_by( 'name', 'featured', 'product_visibility' );
1332
1333 if ( $featured_term ) {
1334 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_featured' AND meta_value = 'yes';", $featured_term->term_taxonomy_id ) );
1335 }
1336
1337 $exclude_search_term = get_term_by( 'name', 'exclude-from-search', 'product_visibility' );
1338
1339 if ( $exclude_search_term ) {
1340 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_visibility' AND meta_value IN ('hidden', 'catalog');", $exclude_search_term->term_taxonomy_id ) );
1341 }
1342
1343 $exclude_catalog_term = get_term_by( 'name', 'exclude-from-catalog', 'product_visibility' );
1344
1345 if ( $exclude_catalog_term ) {
1346 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_visibility' AND meta_value IN ('hidden', 'search');", $exclude_catalog_term->term_taxonomy_id ) );
1347 }
1348
1349 $outofstock_term = get_term_by( 'name', ProductStockStatus::OUT_OF_STOCK, 'product_visibility' );
1350
1351 if ( $outofstock_term ) {
1352 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_stock_status' AND meta_value = 'outofstock';", $outofstock_term->term_taxonomy_id ) );
1353 }
1354
1355 $rating_term = get_term_by( 'name', 'rated-1', 'product_visibility' );
1356
1357 if ( $rating_term ) {
1358 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_wc_average_rating' AND ROUND( meta_value ) = 1;", $rating_term->term_taxonomy_id ) );
1359 }
1360
1361 $rating_term = get_term_by( 'name', 'rated-2', 'product_visibility' );
1362
1363 if ( $rating_term ) {
1364 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_wc_average_rating' AND ROUND( meta_value ) = 2;", $rating_term->term_taxonomy_id ) );
1365 }
1366
1367 $rating_term = get_term_by( 'name', 'rated-3', 'product_visibility' );
1368
1369 if ( $rating_term ) {
1370 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_wc_average_rating' AND ROUND( meta_value ) = 3;", $rating_term->term_taxonomy_id ) );
1371 }
1372
1373 $rating_term = get_term_by( 'name', 'rated-4', 'product_visibility' );
1374
1375 if ( $rating_term ) {
1376 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_wc_average_rating' AND ROUND( meta_value ) = 4;", $rating_term->term_taxonomy_id ) );
1377 }
1378
1379 $rating_term = get_term_by( 'name', 'rated-5', 'product_visibility' );
1380
1381 if ( $rating_term ) {
1382 $wpdb->query( $wpdb->prepare( "INSERT IGNORE INTO {$wpdb->term_relationships} SELECT post_id, %d, 0 FROM {$wpdb->postmeta} WHERE meta_key = '_wc_average_rating' AND ROUND( meta_value ) = 5;", $rating_term->term_taxonomy_id ) );
1383 }
1384 }
1385
1386 /**
1387 * Update DB Version.
1388 *
1389 * @return void
1390 */
1391 function wc_update_300_db_version() {
1392 WC_Install::update_db_version( '3.0.0' );
1393 }
1394
1395 /**
1396 * Add an index to the downloadable product permissions table to improve performance of update_user_by_order_id.
1397 *
1398 * @return void
1399 */
1400 function wc_update_310_downloadable_products() {
1401 global $wpdb;
1402
1403 $index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE column_name = 'order_id' and key_name = 'order_id'" );
1404
1405 if ( is_null( $index_exists ) ) {
1406 $wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions ADD INDEX order_id (order_id)" );
1407 }
1408 }
1409
1410 /**
1411 * Find old order notes and ensure they have the correct type for exclusion.
1412 *
1413 * @return void
1414 */
1415 function wc_update_310_old_comments() {
1416 global $wpdb;
1417
1418 $wpdb->query( "UPDATE $wpdb->comments comments LEFT JOIN $wpdb->posts as posts ON comments.comment_post_ID = posts.ID SET comment_type = 'order_note' WHERE posts.post_type = 'shop_order' AND comment_type = '';" );
1419 }
1420
1421 /**
1422 * Update DB Version.
1423 *
1424 * @return void
1425 */
1426 function wc_update_310_db_version() {
1427 WC_Install::update_db_version( '3.1.0' );
1428 }
1429
1430 /**
1431 * Update shop_manager capabilities.
1432 *
1433 * @return void
1434 */
1435 function wc_update_312_shop_manager_capabilities() {
1436 $role = get_role( 'shop_manager' );
1437 $role->remove_cap( 'unfiltered_html' );
1438 }
1439
1440 /**
1441 * Update DB Version.
1442 *
1443 * @return void
1444 */
1445 function wc_update_312_db_version() {
1446 WC_Install::update_db_version( '3.1.2' );
1447 }
1448
1449 /**
1450 * Update state codes for Mexico.
1451 *
1452 * @return void
1453 */
1454 function wc_update_320_mexican_states() {
1455 global $wpdb;
1456
1457 $mx_states = array(
1458 'Distrito Federal' => 'CMX',
1459 'Jalisco' => 'JAL',
1460 'Nuevo Leon' => 'NLE',
1461 'Aguascalientes' => 'AGS',
1462 'Baja California' => 'BCN',
1463 'Baja California Sur' => 'BCS',
1464 'Campeche' => 'CAM',
1465 'Chiapas' => 'CHP',
1466 'Chihuahua' => 'CHH',
1467 'Coahuila' => 'COA',
1468 'Colima' => 'COL',
1469 'Durango' => 'DGO',
1470 'Guanajuato' => 'GTO',
1471 'Guerrero' => 'GRO',
1472 'Hidalgo' => 'HGO',
1473 'Estado de Mexico' => 'MEX',
1474 'Michoacan' => 'MIC',
1475 'Morelos' => 'MOR',
1476 'Nayarit' => 'NAY',
1477 'Oaxaca' => 'OAX',
1478 'Puebla' => 'PUE',
1479 'Queretaro' => 'QRO',
1480 'Quintana Roo' => 'ROO',
1481 'San Luis Potosi' => 'SLP',
1482 'Sinaloa' => 'SIN',
1483 'Sonora' => 'SON',
1484 'Tabasco' => 'TAB',
1485 'Tamaulipas' => 'TMP',
1486 'Tlaxcala' => 'TLA',
1487 'Veracruz' => 'VER',
1488 'Yucatan' => 'YUC',
1489 'Zacatecas' => 'ZAC',
1490 );
1491
1492 foreach ( $mx_states as $old => $new ) {
1493 $wpdb->query(
1494 $wpdb->prepare(
1495 "UPDATE $wpdb->postmeta
1496 SET meta_value = %s
1497 WHERE meta_key IN ( '_billing_state', '_shipping_state' )
1498 AND meta_value = %s",
1499 $new,
1500 $old
1501 )
1502 );
1503 $wpdb->update(
1504 "{$wpdb->prefix}woocommerce_shipping_zone_locations",
1505 array(
1506 'location_code' => 'MX:' . $new,
1507 ),
1508 array(
1509 'location_code' => 'MX:' . $old,
1510 )
1511 );
1512 $wpdb->update(
1513 "{$wpdb->prefix}woocommerce_tax_rates",
1514 array(
1515 'tax_rate_state' => strtoupper( $new ),
1516 ),
1517 array(
1518 'tax_rate_state' => strtoupper( $old ),
1519 )
1520 );
1521 }
1522 }
1523
1524 /**
1525 * Update DB Version.
1526 *
1527 * @return void
1528 */
1529 function wc_update_320_db_version() {
1530 WC_Install::update_db_version( '3.2.0' );
1531 }
1532
1533 /**
1534 * Update image settings to use new aspect ratios and widths.
1535 *
1536 * @return void
1537 */
1538 function wc_update_330_image_options() {
1539 $old_thumbnail_size = get_option( 'shop_catalog_image_size', array() );
1540 $old_single_size = get_option( 'shop_single_image_size', array() );
1541
1542 if ( ! empty( $old_thumbnail_size['width'] ) ) {
1543 $width = absint( $old_thumbnail_size['width'] );
1544 $height = absint( $old_thumbnail_size['height'] );
1545 $hard_crop = ! empty( $old_thumbnail_size['crop'] );
1546
1547 if ( ! $width ) {
1548 $width = 300;
1549 }
1550
1551 if ( ! $height ) {
1552 $height = $width;
1553 }
1554
1555 update_option( 'woocommerce_thumbnail_image_width', $width );
1556
1557 // Calculate cropping mode from old image options.
1558 if ( ! $hard_crop ) {
1559 update_option( 'woocommerce_thumbnail_cropping', 'uncropped' );
1560 } elseif ( $width === $height ) {
1561 update_option( 'woocommerce_thumbnail_cropping', '1:1' );
1562 } else {
1563 $ratio = $width / $height;
1564 $fraction = wc_decimal_to_fraction( $ratio );
1565
1566 if ( $fraction ) {
1567 update_option( 'woocommerce_thumbnail_cropping', 'custom' );
1568 update_option( 'woocommerce_thumbnail_cropping_custom_width', $fraction[0] );
1569 update_option( 'woocommerce_thumbnail_cropping_custom_height', $fraction[1] );
1570 }
1571 }
1572 }
1573
1574 // Single is uncropped.
1575 if ( ! empty( $old_single_size['width'] ) ) {
1576 update_option( 'woocommerce_single_image_width', absint( $old_single_size['width'] ) );
1577 }
1578 }
1579
1580 /**
1581 * Migrate webhooks from post type to CRUD.
1582 *
1583 * @return void
1584 */
1585 function wc_update_330_webhooks() {
1586 register_post_type( 'shop_webhook' );
1587
1588 // Map statuses from post_type to Webhooks CRUD.
1589 $statuses = array(
1590 'publish' => 'active',
1591 'draft' => 'paused',
1592 'pending' => 'disabled',
1593 );
1594
1595 $posts = get_posts(
1596 array(
1597 'posts_per_page' => -1,
1598 'post_type' => 'shop_webhook',
1599 'post_status' => 'any',
1600 )
1601 );
1602
1603 foreach ( $posts as $post ) {
1604 $webhook = new WC_Webhook();
1605 $webhook->set_name( $post->post_title );
1606 $webhook->set_status( isset( $statuses[ $post->post_status ] ) ? $statuses[ $post->post_status ] : 'disabled' );
1607 $webhook->set_delivery_url( get_post_meta( $post->ID, '_delivery_url', true ) );
1608 $webhook->set_secret( get_post_meta( $post->ID, '_secret', true ) );
1609 $webhook->set_topic( get_post_meta( $post->ID, '_topic', true ) );
1610 $webhook->set_api_version( get_post_meta( $post->ID, '_api_version', true ) );
1611 $webhook->set_user_id( $post->post_author );
1612 $webhook->set_pending_delivery( false );
1613 $webhook->save();
1614
1615 wp_delete_post( $post->ID, true );
1616 }
1617
1618 unregister_post_type( 'shop_webhook' );
1619 }
1620
1621 /**
1622 * Assign default cat to all products with no cats.
1623 *
1624 * @return void
1625 */
1626 function wc_update_330_set_default_product_cat() {
1627 /*
1628 * When a product category is deleted, we need to check
1629 * if the product has no categories assigned. Then assign
1630 * it a default category.
1631 */
1632 wc_get_container()->get( AssignDefaultCategory::class )->maybe_assign_default_product_cat();
1633 }
1634
1635 /**
1636 * Update product stock status to use the new onbackorder status.
1637 *
1638 * @return void
1639 */
1640 function wc_update_330_product_stock_status() {
1641 global $wpdb;
1642
1643 if ( 'yes' !== get_option( 'woocommerce_manage_stock' ) ) {
1644 return;
1645 }
1646
1647 $min_stock_amount = (int) get_option( 'woocommerce_notify_no_stock_amount', 0 );
1648
1649 // Get all products that have stock management enabled, stock less than or equal to min stock amount, and backorders enabled.
1650 $post_ids = $wpdb->get_col(
1651 $wpdb->prepare(
1652 "SELECT t1.post_id FROM $wpdb->postmeta t1
1653 INNER JOIN $wpdb->postmeta t2
1654 ON t1.post_id = t2.post_id
1655 AND t1.meta_key = '_manage_stock' AND t1.meta_value = 'yes'
1656 AND t2.meta_key = '_stock' AND t2.meta_value <= %d
1657 INNER JOIN $wpdb->postmeta t3
1658 ON t2.post_id = t3.post_id
1659 AND t3.meta_key = '_backorders' AND ( t3.meta_value = 'yes' OR t3.meta_value = 'notify' )",
1660 $min_stock_amount
1661 )
1662 );
1663
1664 if ( empty( $post_ids ) ) {
1665 return;
1666 }
1667
1668 $post_ids = array_map( 'absint', $post_ids );
1669
1670 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
1671 // Set the status to onbackorder for those products.
1672 $wpdb->query(
1673 "UPDATE $wpdb->postmeta
1674 SET meta_value = 'onbackorder'
1675 WHERE meta_key = '_stock_status' AND post_id IN ( " . implode( ',', $post_ids ) . ' )'
1676 );
1677 // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
1678 }
1679
1680 /**
1681 * Clear addons page transients
1682 *
1683 * @return void
1684 */
1685 function wc_update_330_clear_transients() {
1686 delete_transient( 'wc_addons_sections' );
1687 delete_transient( 'wc_addons_featured' );
1688 }
1689
1690 /**
1691 * Set PayPal's sandbox credentials.
1692 *
1693 * @return void
1694 */
1695 function wc_update_330_set_paypal_sandbox_credentials() {
1696
1697 $paypal_settings = get_option( 'woocommerce_paypal_settings' );
1698
1699 if ( isset( $paypal_settings['testmode'] ) && 'yes' === $paypal_settings['testmode'] ) {
1700 foreach ( array( 'api_username', 'api_password', 'api_signature' ) as $credential ) {
1701 if ( ! empty( $paypal_settings[ $credential ] ) ) {
1702 $paypal_settings[ 'sandbox_' . $credential ] = $paypal_settings[ $credential ];
1703 }
1704 }
1705
1706 update_option( 'woocommerce_paypal_settings', $paypal_settings );
1707 }
1708 }
1709
1710 /**
1711 * Update DB Version.
1712 *
1713 * @return void
1714 */
1715 function wc_update_330_db_version() {
1716 WC_Install::update_db_version( '3.3.0' );
1717 }
1718
1719 /**
1720 * Update state codes for Ireland and BD.
1721 *
1722 * @return void
1723 */
1724 function wc_update_340_states() {
1725 $country_states = array(
1726 'IE' => array(
1727 'CK' => 'CO',
1728 'DN' => 'D',
1729 'GY' => 'G',
1730 'TY' => 'TA',
1731 ),
1732 'BD' => array(
1733 'BAG' => 'BD-05',
1734 'BAN' => 'BD-01',
1735 'BAR' => 'BD-02',
1736 'BARI' => 'BD-06',
1737 'BHO' => 'BD-07',
1738 'BOG' => 'BD-03',
1739 'BRA' => 'BD-04',
1740 'CHA' => 'BD-09',
1741 'CHI' => 'BD-10',
1742 'CHU' => 'BD-12',
1743 'COX' => 'BD-11',
1744 'COM' => 'BD-08',
1745 'DHA' => 'BD-13',
1746 'DIN' => 'BD-14',
1747 'FAR' => 'BD-15',
1748 'FEN' => 'BD-16',
1749 'GAI' => 'BD-19',
1750 'GAZI' => 'BD-18',
1751 'GOP' => 'BD-17',
1752 'HAB' => 'BD-20',
1753 'JAM' => 'BD-21',
1754 'JES' => 'BD-22',
1755 'JHA' => 'BD-25',
1756 'JHE' => 'BD-23',
1757 'JOY' => 'BD-24',
1758 'KHA' => 'BD-29',
1759 'KHU' => 'BD-27',
1760 'KIS' => 'BD-26',
1761 'KUR' => 'BD-28',
1762 'KUS' => 'BD-30',
1763 'LAK' => 'BD-31',
1764 'LAL' => 'BD-32',
1765 'MAD' => 'BD-36',
1766 'MAG' => 'BD-37',
1767 'MAN' => 'BD-33',
1768 'MEH' => 'BD-39',
1769 'MOU' => 'BD-38',
1770 'MUN' => 'BD-35',
1771 'MYM' => 'BD-34',
1772 'NAO' => 'BD-48',
1773 'NAR' => 'BD-43',
1774 'NARG' => 'BD-40',
1775 'NARD' => 'BD-42',
1776 'NAT' => 'BD-44',
1777 'NAW' => 'BD-45',
1778 'NET' => 'BD-41',
1779 'NIL' => 'BD-46',
1780 'NOA' => 'BD-47',
1781 'PAB' => 'BD-49',
1782 'PAN' => 'BD-52',
1783 'PAT' => 'BD-51',
1784 'PIR' => 'BD-50',
1785 'RAJB' => 'BD-53',
1786 'RAJ' => 'BD-54',
1787 'RAN' => 'BD-56',
1788 'RANP' => 'BD-55',
1789 'SAT' => 'BD-58',
1790 'SHA' => 'BD-57',
1791 'SIR' => 'BD-59',
1792 'SUN' => 'BD-61',
1793 'SYL' => 'BD-60',
1794 'TAN' => 'BD-63',
1795 'THA' => 'BD-64',
1796 ),
1797 );
1798
1799 update_option( 'woocommerce_update_340_states', $country_states );
1800 }
1801
1802 /**
1803 * Update next state in the queue.
1804 *
1805 * @return bool True to run again, false if completed.
1806 */
1807 function wc_update_340_state() {
1808 global $wpdb;
1809
1810 $country_states = array_filter( (array) get_option( 'woocommerce_update_340_states', array() ) );
1811
1812 if ( empty( $country_states ) ) {
1813 return false;
1814 }
1815
1816 foreach ( $country_states as $country => $states ) {
1817 foreach ( $states as $old => $new ) {
1818 $wpdb->query(
1819 $wpdb->prepare(
1820 "UPDATE $wpdb->postmeta
1821 SET meta_value = %s
1822 WHERE meta_key IN ( '_billing_state', '_shipping_state' )
1823 AND meta_value = %s",
1824 $new,
1825 $old
1826 )
1827 );
1828 $wpdb->update(
1829 "{$wpdb->prefix}woocommerce_shipping_zone_locations",
1830 array(
1831 'location_code' => $country . ':' . $new,
1832 ),
1833 array(
1834 'location_code' => $country . ':' . $old,
1835 )
1836 );
1837 $wpdb->update(
1838 "{$wpdb->prefix}woocommerce_tax_rates",
1839 array(
1840 'tax_rate_state' => strtoupper( $new ),
1841 ),
1842 array(
1843 'tax_rate_state' => strtoupper( $old ),
1844 )
1845 );
1846 unset( $country_states[ $country ][ $old ] );
1847
1848 if ( empty( $country_states[ $country ] ) ) {
1849 unset( $country_states[ $country ] );
1850 }
1851 break 2;
1852 }
1853 }
1854
1855 if ( ! empty( $country_states ) ) {
1856 return update_option( 'woocommerce_update_340_states', $country_states );
1857 }
1858
1859 delete_option( 'woocommerce_update_340_states' );
1860
1861 return false;
1862 }
1863
1864 /**
1865 * Set last active prop for users.
1866 *
1867 * @return void
1868 */
1869 function wc_update_340_last_active() {
1870 global $wpdb;
1871 // @codingStandardsIgnoreStart.
1872 $wpdb->query(
1873 $wpdb->prepare( "
1874 INSERT INTO {$wpdb->usermeta} (user_id, meta_key, meta_value)
1875 SELECT DISTINCT users.ID, 'wc_last_active', %s
1876 FROM {$wpdb->users} as users
1877 LEFT OUTER JOIN {$wpdb->usermeta} AS usermeta ON users.ID = usermeta.user_id AND usermeta.meta_key = 'wc_last_active'
1878 WHERE usermeta.meta_value IS NULL
1879 ",
1880 (string) strtotime( date( 'Y-m-d', current_time( 'timestamp', true ) ) )
1881 )
1882 );
1883 // @codingStandardsIgnoreEnd.
1884 }
1885
1886 /**
1887 * Update DB Version.
1888 *
1889 * @return void
1890 */
1891 function wc_update_340_db_version() {
1892 WC_Install::update_db_version( '3.4.0' );
1893 }
1894
1895 /**
1896 * Remove duplicate foreign keys
1897 *
1898 * @return void
1899 */
1900 function wc_update_343_cleanup_foreign_keys() {
1901 global $wpdb;
1902
1903 $create_table_sql = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 );
1904
1905 if ( ! empty( $create_table_sql ) ) {
1906 // Extract and remove the foreign key constraints matching %wc_download_log_ib%.
1907 if ( preg_match_all( '/CONSTRAINT `([^`]*wc_download_log_ib[^`]*)` FOREIGN KEY/', $create_table_sql, $matches ) && ! empty( $matches[1] ) ) {
1908 foreach ( $matches[1] as $foreign_key_name ) {
1909 $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY `{$foreign_key_name}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1910 }
1911 }
1912 }
1913 }
1914
1915 /**
1916 * Update DB version.
1917 *
1918 * @return void
1919 */
1920 function wc_update_343_db_version() {
1921 WC_Install::update_db_version( '3.4.3' );
1922 }
1923
1924 /**
1925 * Recreate user roles so existing users will get the new capabilities.
1926 *
1927 * @return void
1928 */
1929 function wc_update_344_recreate_roles() {
1930 WC_Install::remove_roles();
1931 WC_Install::create_roles();
1932 }
1933
1934 /**
1935 * Update DB version.
1936 *
1937 * @return void
1938 */
1939 function wc_update_344_db_version() {
1940 WC_Install::update_db_version( '3.4.4' );
1941 }
1942
1943 /**
1944 * Set the comment type to 'review' for product reviews that don't have a comment type.
1945 *
1946 * @return void
1947 */
1948 function wc_update_350_reviews_comment_type() {
1949 global $wpdb;
1950
1951 $wpdb->query(
1952 "UPDATE {$wpdb->prefix}comments JOIN {$wpdb->prefix}posts ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}comments.comment_post_ID AND ( {$wpdb->prefix}posts.post_type = 'product' OR {$wpdb->prefix}posts.post_type = 'product_variation' ) SET {$wpdb->prefix}comments.comment_type = 'review' WHERE {$wpdb->prefix}comments.comment_type = ''"
1953 );
1954 }
1955
1956 /**
1957 * Update DB Version.
1958 *
1959 * @return void
1960 */
1961 function wc_update_350_db_version() {
1962 WC_Install::update_db_version( '3.5.0' );
1963 }
1964
1965 /**
1966 * Drop the fk_wc_download_log_permission_id FK as we use a new one with the table and blog prefix for MS compatibility.
1967 *
1968 * @return void
1969 */
1970 function wc_update_352_drop_download_log_fk() {
1971 global $wpdb;
1972
1973 $create_table_sql = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 );
1974
1975 if ( ! empty( $create_table_sql ) ) {
1976 if ( strpos( $create_table_sql, 'CONSTRAINT `fk_wc_download_log_permission_id` FOREIGN KEY' ) !== false ) {
1977 $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY fk_wc_download_log_permission_id" ); // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared
1978 }
1979 }
1980 }
1981
1982 /**
1983 * Remove edit_user capabilities from shop managers and use "translated" capabilities instead.
1984 * See wc_shop_manager_has_capability function.
1985 *
1986 * @return void
1987 */
1988 function wc_update_354_modify_shop_manager_caps() {
1989 global $wp_roles;
1990
1991 if ( ! class_exists( 'WP_Roles' ) ) {
1992 return;
1993 }
1994
1995 if ( ! isset( $wp_roles ) ) {
1996 $wp_roles = new WP_Roles(); // @codingStandardsIgnoreLine
1997 }
1998
1999 $wp_roles->remove_cap( 'shop_manager', 'edit_users' );
2000 }
2001
2002 /**
2003 * Update DB Version.
2004 *
2005 * @return void
2006 */
2007 function wc_update_354_db_version() {
2008 WC_Install::update_db_version( '3.5.4' );
2009 }
2010
2011 /**
2012 * Update product lookup tables in bulk.
2013 *
2014 * @return void
2015 */
2016 function wc_update_360_product_lookup_tables() {
2017 wc_update_product_lookup_tables();
2018 }
2019
2020 /**
2021 * Renames ordering meta to be consistent across taxonomies.
2022 *
2023 * @return void
2024 */
2025 function wc_update_360_term_meta() {
2026 global $wpdb;
2027
2028 $wpdb->query( "UPDATE {$wpdb->termmeta} SET meta_key = 'order' WHERE meta_key LIKE 'order_pa_%';" );
2029 }
2030
2031 /**
2032 * Add new user_order_remaining_expires to speed up user download permission fetching.
2033 *
2034 * @return void
2035 */
2036 function wc_update_360_downloadable_product_permissions_index() {
2037 global $wpdb;
2038
2039 $index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE key_name = 'user_order_remaining_expires'" );
2040
2041 if ( is_null( $index_exists ) ) {
2042 $wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions ADD INDEX user_order_remaining_expires (user_id,order_id,downloads_remaining,access_expires)" );
2043 }
2044 }
2045
2046 /**
2047 * Update DB Version.
2048 *
2049 * @return void
2050 */
2051 function wc_update_360_db_version() {
2052 WC_Install::update_db_version( '3.6.0' );
2053 }
2054
2055 /**
2056 * Put tax classes into a DB table.
2057 *
2058 * @return void
2059 */
2060 function wc_update_370_tax_rate_classes() {
2061 global $wpdb;
2062
2063 $classes = array_map( 'trim', explode( "\n", get_option( 'woocommerce_tax_classes' ) ) );
2064
2065 if ( $classes ) {
2066 foreach ( $classes as $class ) {
2067 if ( empty( $class ) ) {
2068 continue;
2069 }
2070 WC_Tax::create_tax_class( $class );
2071 }
2072 }
2073 delete_option( 'woocommerce_tax_classes' );
2074 }
2075
2076 /**
2077 * Update currency settings for 3.7.0
2078 *
2079 * @return void
2080 */
2081 function wc_update_370_mro_std_currency() {
2082 global $wpdb;
2083
2084 // Fix currency settings for MRU and STN currency.
2085 $current_currency = get_option( 'woocommerce_currency' );
2086
2087 if ( 'MRO' === $current_currency ) {
2088 update_option( 'woocommerce_currency', 'MRU' );
2089 }
2090
2091 if ( 'STD' === $current_currency ) {
2092 update_option( 'woocommerce_currency', 'STN' );
2093 }
2094
2095 // Update MRU currency code.
2096 $wpdb->update(
2097 $wpdb->postmeta,
2098 array(
2099 'meta_value' => 'MRU', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
2100 ),
2101 array(
2102 'meta_key' => '_order_currency', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
2103 'meta_value' => 'MRO', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
2104 )
2105 );
2106
2107 // Update STN currency code.
2108 $wpdb->update(
2109 $wpdb->postmeta,
2110 array(
2111 'meta_value' => 'STN', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
2112 ),
2113 array(
2114 'meta_key' => '_order_currency', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
2115 'meta_value' => 'STD', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
2116 )
2117 );
2118 }
2119
2120 /**
2121 * Update DB Version.
2122 *
2123 * @return void
2124 */
2125 function wc_update_370_db_version() {
2126 WC_Install::update_db_version( '3.7.0' );
2127 }
2128
2129 /**
2130 * We've moved the MaxMind database to a new location, as per the TOS' requirement that the database not
2131 * be publicly accessible.
2132 *
2133 * @return void
2134 */
2135 function wc_update_390_move_maxmind_database() {
2136 // Make sure to use all of the correct filters to pull the local database path.
2137 $old_path = apply_filters( 'woocommerce_geolocation_local_database_path', WP_CONTENT_DIR . '/uploads/GeoLite2-Country.mmdb', 2 );
2138
2139 // Generate a prefix for the old file and store it in the integration as it would expect it.
2140 $prefix = wp_generate_password( 32, false );
2141 update_option( 'woocommerce_maxmind_geolocation_settings', array( 'database_prefix' => $prefix ) );
2142
2143 // Generate the new path in the same way that the integration will.
2144 $uploads_dir = wp_upload_dir();
2145 $new_path = trailingslashit( $uploads_dir['basedir'] ) . 'woocommerce_uploads/' . $prefix . '-GeoLite2-Country.mmdb';
2146 $new_path = apply_filters( 'woocommerce_geolocation_local_database_path', $new_path, 2 );
2147 $new_path = apply_filters( 'woocommerce_maxmind_geolocation_database_path', $new_path );
2148
2149 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
2150 @rename( $old_path, $new_path );
2151 }
2152
2153 /**
2154 * So that we can best meet MaxMind's TOS, the geolocation database update cron should run once per 15 days.
2155 *
2156 * @return void
2157 */
2158 function wc_update_390_change_geolocation_database_update_cron() {
2159 wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
2160 wp_schedule_event( time() + ( DAY_IN_SECONDS * 15 ), 'fifteendays', 'woocommerce_geoip_updater' );
2161 }
2162
2163 /**
2164 * Update DB version.
2165 *
2166 * @return void
2167 */
2168 function wc_update_390_db_version() {
2169 WC_Install::update_db_version( '3.9.0' );
2170 }
2171
2172 /**
2173 * Increase column size
2174 *
2175 * @return void
2176 */
2177 function wc_update_400_increase_size_of_column() {
2178 global $wpdb;
2179 $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_product_meta_lookup MODIFY COLUMN `min_price` decimal(19,4) NULL default NULL" );
2180 $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_product_meta_lookup MODIFY COLUMN `max_price` decimal(19,4) NULL default NULL" );
2181 }
2182
2183 /**
2184 * Reset ActionScheduler migration status. Needs AS >= 3.0 shipped with WC >= 4.0.
2185 *
2186 * @return void
2187 */
2188 function wc_update_400_reset_action_scheduler_migration_status() {
2189 if (
2190 class_exists( 'ActionScheduler_DataController' ) &&
2191 method_exists( 'ActionScheduler_DataController', 'mark_migration_incomplete' ) // @phpstan-ignore function.alreadyNarrowedType
2192 ) {
2193 \ActionScheduler_DataController::mark_migration_incomplete();
2194 }
2195 }
2196
2197 /**
2198 * Update DB version.
2199 *
2200 * @return void
2201 */
2202 function wc_update_400_db_version() {
2203 WC_Install::update_db_version( '4.0.0' );
2204 }
2205
2206 /**
2207 * Register attributes as terms for variable products, in increments of 100 products.
2208 *
2209 * This migration was added to support a new mechanism to improve the filtering of
2210 * variable products by attribute (https://github.com/woocommerce/woocommerce/pull/26260),
2211 * however that mechanism was later reverted (https://github.com/woocommerce/woocommerce/pull/27625)
2212 * due to numerous issues found. Thus the migration is no longer needed.
2213 *
2214 * @return bool true if the migration needs to be run again.
2215 */
2216 function wc_update_440_insert_attribute_terms_for_variable_products() {
2217 return false;
2218 }
2219
2220 /**
2221 * Update DB version.
2222 *
2223 * @return void
2224 */
2225 function wc_update_440_db_version() {
2226 WC_Install::update_db_version( '4.4.0' );
2227 }
2228
2229 /**
2230 * Update DB version to 4.5.0.
2231 *
2232 * @return void
2233 */
2234 function wc_update_450_db_version() {
2235 WC_Install::update_db_version( '4.5.0' );
2236 }
2237
2238 /**
2239 * Sanitize all coupons code.
2240 *
2241 * @return bool True to run again, false if completed.
2242 */
2243 function wc_update_450_sanitize_coupons_code() {
2244 global $wpdb;
2245
2246 $coupon_id = 0;
2247 $last_coupon_id = get_option( 'woocommerce_update_450_last_coupon_id', '0' );
2248
2249 $coupons = $wpdb->get_results(
2250 $wpdb->prepare(
2251 "SELECT ID, post_title FROM $wpdb->posts WHERE ID > %d AND post_type = 'shop_coupon' LIMIT 10",
2252 $last_coupon_id
2253 ),
2254 ARRAY_A
2255 );
2256
2257 if ( empty( $coupons ) ) {
2258 delete_option( 'woocommerce_update_450_last_coupon_id' );
2259 return false;
2260 }
2261
2262 foreach ( $coupons as $key => $data ) {
2263 $coupon_id = intval( $data['ID'] );
2264 $code = trim( wp_filter_kses( $data['post_title'] ) );
2265
2266 if ( ! empty( $code ) && $data['post_title'] !== $code ) {
2267 $wpdb->update(
2268 $wpdb->posts,
2269 array(
2270 'post_title' => $code,
2271 ),
2272 array(
2273 'ID' => $coupon_id,
2274 ),
2275 array(
2276 '%s',
2277 ),
2278 array(
2279 '%d',
2280 )
2281 );
2282
2283 // Clean cache.
2284 clean_post_cache( $coupon_id );
2285 wp_cache_delete( WC_Cache_Helper::get_cache_prefix( 'coupons' ) . 'coupon_id_from_code_' . $data['post_title'], 'coupons' );
2286 }
2287 }
2288
2289 // Start the run again.
2290 if ( $coupon_id ) {
2291 return update_option( 'woocommerce_update_450_last_coupon_id', $coupon_id );
2292 }
2293
2294 delete_option( 'woocommerce_update_450_last_coupon_id' );
2295 return false;
2296 }
2297
2298 /**
2299 * Fixes product review count that might have been incorrect.
2300 *
2301 * See @link https://github.com/woocommerce/woocommerce/issues/27688.
2302 *
2303 * @return bool True to run again, false if completed.
2304 */
2305 function wc_update_500_fix_product_review_count() {
2306 global $wpdb;
2307
2308 $product_id = 0;
2309 $last_product_id = get_option( 'woocommerce_update_500_last_product_id', '0' );
2310
2311 $products_data = $wpdb->get_results(
2312 $wpdb->prepare(
2313 "
2314 SELECT post_id, meta_value
2315 FROM $wpdb->postmeta
2316 JOIN $wpdb->posts
2317 ON $wpdb->postmeta.post_id = $wpdb->posts.ID
2318 WHERE
2319 post_type = 'product'
2320 AND post_status = 'publish'
2321 AND post_id > %d
2322 AND meta_key = '_wc_review_count'
2323 ORDER BY post_id ASC
2324 LIMIT 10
2325 ",
2326 $last_product_id
2327 ),
2328 ARRAY_A
2329 );
2330
2331 if ( empty( $products_data ) ) {
2332 delete_option( 'woocommerce_update_500_last_product_id' );
2333 return false;
2334 }
2335
2336 $product_ids_to_check = array_column( $products_data, 'post_id' );
2337 $actual_review_counts = WC_Comments::get_review_counts_for_product_ids( $product_ids_to_check );
2338
2339 foreach ( $products_data as $product_data ) {
2340 $product_id = intval( $product_data['post_id'] );
2341 $current_review_count = intval( $product_data['meta_value'] );
2342
2343 if ( intval( $actual_review_counts[ $product_id ] ) !== $current_review_count ) {
2344 WC_Comments::clear_transients( $product_id );
2345 }
2346 }
2347
2348 // Start the run again.
2349 if ( $product_id ) {
2350 return update_option( 'woocommerce_update_500_last_product_id', $product_id );
2351 }
2352
2353 delete_option( 'woocommerce_update_500_last_product_id' );
2354 return false;
2355 }
2356
2357 /**
2358 * Update DB version to 5.0.0.
2359 *
2360 * @return void
2361 */
2362 function wc_update_500_db_version() {
2363 WC_Install::update_db_version( '5.0.0' );
2364 }
2365
2366 // phpcs:disable Squiz.Commenting.FunctionComment.InvalidReturnVoid -- return statement is in a nested function, not this one.
2367 /**
2368 * Creates the refund and returns policy page.
2369 *
2370 * See @link https://github.com/woocommerce/woocommerce/issues/29235.
2371 *
2372 * @return void
2373 */
2374 function wc_update_560_create_refund_returns_page() {
2375 // phpcs:enable Squiz.Commenting.FunctionComment.InvalidReturnVoid
2376 /**
2377 * Filter on the pages created to return what we expect.
2378 *
2379 * @param array $pages The default WC pages.
2380 * @return array
2381 */
2382 function filter_created_pages( $pages ) {
2383 $page_to_create = array( 'refund_returns' );
2384
2385 return array_intersect_key( $pages, array_flip( $page_to_create ) );
2386 }
2387
2388 add_filter( 'woocommerce_create_pages', 'filter_created_pages' );
2389
2390 WC_Install::create_pages();
2391
2392 remove_filter( 'woocommerce_create_pages', 'filter_created_pages' );
2393 }
2394
2395 /**
2396 * Update DB version to 5.6.0.
2397 *
2398 * @return void
2399 */
2400 function wc_update_560_db_version() {
2401 WC_Install::update_db_version( '5.6.0' );
2402 }
2403
2404 /**
2405 * Migrate rate limit options to the new table.
2406 *
2407 * See @link https://github.com/woocommerce/woocommerce/issues/27103.
2408 *
2409 * @return void
2410 */
2411 function wc_update_600_migrate_rate_limit_options() {
2412 global $wpdb;
2413
2414 $rate_limits = $wpdb->get_results(
2415 "
2416 SELECT option_name, option_value
2417 FROM $wpdb->options
2418 WHERE option_name LIKE 'woocommerce_rate_limit_add_payment_method_%'
2419 ",
2420 ARRAY_A
2421 );
2422 $prefix_length = strlen( 'woocommerce_rate_limit_' );
2423
2424 foreach ( $rate_limits as $rate_limit ) {
2425 $new_delay = (int) $rate_limit['option_value'] - time();
2426
2427 // Migrate the limit if it hasn't expired yet.
2428 if ( 0 < $new_delay ) {
2429 $action_id = substr( $rate_limit['option_name'], $prefix_length );
2430 WC_Rate_Limiter::set_rate_limit( $action_id, $new_delay );
2431 }
2432
2433 delete_option( $rate_limit['option_name'] );
2434 }
2435 }
2436
2437 /**
2438 * Update DB version to 6.0.0.
2439 *
2440 * @return void
2441 */
2442 function wc_update_600_db_version() {
2443 WC_Install::update_db_version( '6.0.0' );
2444 }
2445
2446 /**
2447 * Create the product attributes lookup table and initiate its filling,
2448 * unless the table had been already created manually (via the tools page).
2449 *
2450 * @return false Always false, since the LookupDataStore class handles all the data filling process.
2451 */
2452 function wc_update_630_create_product_attributes_lookup_table() {
2453 $data_store = wc_get_container()->get( LookupDataStore::class );
2454 $data_regenerator = wc_get_container()->get( DataRegenerator::class );
2455
2456 /**
2457 * If the table exists and contains data, it was manually created by user before the migration ran.
2458 * If the table exists but is empty, it was likely created right now via dbDelta, so a table regenerations is needed (unless one is in progress already).
2459 */
2460 if ( ! $data_store->check_lookup_table_exists() || ( ! $data_store->lookup_table_has_data() && ! $data_store->regeneration_is_in_progress() ) ) {
2461 $data_regenerator->initiate_regeneration();
2462 }
2463
2464 return false;
2465 }
2466
2467 /**
2468 *
2469 * Update DB version to 6.3.0.
2470 *
2471 * @return void
2472 */
2473 function wc_update_630_db_version() {
2474 WC_Install::update_db_version( '6.3.0' );
2475 }
2476
2477 /**
2478 * Create the primary key for the product attributes lookup table if it doesn't exist already.
2479 *
2480 * @return bool Always false.
2481 */
2482 function wc_update_640_add_primary_key_to_product_attributes_lookup_table() {
2483 wc_get_container()->get( DataRegenerator::class )->create_table_primary_index();
2484
2485 return false;
2486 }
2487
2488 /**
2489 *
2490 * Update DB version to 6.4.0.
2491 *
2492 * @return void
2493 */
2494 function wc_update_640_db_version() {
2495 WC_Install::update_db_version( '6.4.0' );
2496 }
2497
2498 /**
2499 * Add the standard WooCommerce upload directories to the Approved Product Download Directories list
2500 * and start populating it based on existing product download URLs, but do not enable the feature
2501 * (for existing installations, a site admin should review and make a conscious decision to enable).
2502 *
2503 * @return void
2504 */
2505 function wc_update_650_approved_download_directories() {
2506 $directory_sync = wc_get_container()->get( Download_Directories_Sync::class );
2507 $directory_sync->init_hooks();
2508 $directory_sync->init_feature( true, false );
2509 }
2510
2511 /**
2512 * In some cases, the approved download directories table may not have been successfully created during the update to
2513 * 6.5.0. If this was the case we will need to re-initialize the feature.
2514 *
2515 * @return void
2516 */
2517 function wc_update_651_approved_download_directories() {
2518 global $wpdb;
2519
2520 $download_directories = wc_get_container()->get( Download_Directories::class );
2521 $directory_sync = wc_get_container()->get( Download_Directories_Sync::class );
2522
2523 // Check if at least 1 row exists, without scanning the entire table.
2524 $is_populated = (bool) $wpdb->get_var(
2525 'SELECT 1 FROM ' . $download_directories->get_table() . ' LIMIT 1'
2526 );
2527
2528 // If the table contains rules (or does not yet, but a sync is in-progress) we should do nothing else at this point.
2529 if ( $is_populated || $directory_sync->in_progress() ) {
2530 return;
2531 }
2532
2533 // Otherwise, it seems reasonable to assume that the feature was not initialized as expected during the update to
2534 // 6.5.0. Let's give that another try.
2535 $directory_sync->init_hooks();
2536 $directory_sync->init_feature( true, false );
2537 }
2538
2539 /**
2540 * Purges the comments count cache after 6.7.0 split reviews from the comments page.
2541 *
2542 * @return void
2543 */
2544 function wc_update_670_purge_comments_count_cache() {
2545 if ( ! is_callable( 'WC_Comments::delete_comments_count_cache' ) ) {
2546 return;
2547 }
2548
2549 WC_Comments::delete_comments_count_cache();
2550 }
2551 /**
2552 * Remove unnecessary foreign keys.
2553 *
2554 * @return void
2555 */
2556 function wc_update_700_remove_download_log_fk() {
2557 global $wpdb;
2558
2559 $create_table_sql = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 );
2560
2561 if ( ! empty( $create_table_sql ) ) {
2562 if ( preg_match_all( '/CONSTRAINT `([^`]*)` FOREIGN KEY/', $create_table_sql, $matches ) && ! empty( $matches[1] ) ) {
2563 foreach ( $matches[1] as $foreign_key_name ) {
2564 $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY `{$foreign_key_name}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
2565 }
2566 }
2567 }
2568 }
2569
2570 /**
2571 * Remove the transient data for recommended marketing extensions.
2572 *
2573 * @return void
2574 */
2575 function wc_update_700_remove_recommended_marketing_plugins_transient() {
2576 delete_transient( 'wc_marketing_recommended_plugins' );
2577 }
2578
2579 /**
2580 * Update the New Zealand state codes in the database
2581 * after they were updated in code to the CLDR standard.
2582 *
2583 * @return bool True if there are more records that need to be migrated, false otherwise.
2584 */
2585 function wc_update_721_adjust_new_zealand_states() {
2586 return MigrationHelper::migrate_country_states(
2587 'NZ',
2588 array(
2589 'NL' => 'NTL',
2590 'AK' => 'AUK',
2591 'WA' => 'WKO',
2592 'BP' => 'BOP',
2593 'TK' => 'TKI',
2594 'GI' => 'GIS',
2595 'HB' => 'HKB',
2596 'MW' => 'MWT',
2597 'WE' => 'WGN',
2598 'NS' => 'NSN',
2599 'MB' => 'MBH',
2600 'TM' => 'TAS',
2601 'WC' => 'WTC',
2602 'CT' => 'CAN',
2603 'OT' => 'OTA',
2604 'SL' => 'STL',
2605 )
2606 );
2607 }
2608
2609 /**
2610 * Update the Ukraine state codes in the database
2611 * after they were updated in code to the CLDR standard.
2612 *
2613 * @return bool True if there are more records that need to be migrated, false otherwise.
2614 */
2615 function wc_update_721_adjust_ukraine_states() {
2616 return MigrationHelper::migrate_country_states(
2617 'UA',
2618 array(
2619 'VN' => 'UA05',
2620 'LH' => 'UA09',
2621 'VL' => 'UA07',
2622 'DP' => 'UA12',
2623 'DT' => 'UA14',
2624 'ZT' => 'UA18',
2625 'ZK' => 'UA21',
2626 'ZP' => 'UA23',
2627 'IF' => 'UA26',
2628 'KV' => 'UA32',
2629 'KH' => 'UA35',
2630 'LV' => 'UA46',
2631 'MY' => 'UA48',
2632 'OD' => 'UA51',
2633 'PL' => 'UA53',
2634 'RV' => 'UA56',
2635 'SM' => 'UA59',
2636 'TP' => 'UA61',
2637 'KK' => 'UA63',
2638 'KS' => 'UA65',
2639 'KM' => 'UA68',
2640 'CK' => 'UA71',
2641 'CH' => 'UA74',
2642 'CV' => 'UA77',
2643 )
2644 );
2645 }
2646
2647 /**
2648 * Update the New Zealand state codes in the database after they were updated in code to the CLDR standard.
2649 *
2650 * This is a simple wrapper for the corresponding 7.2.1 update function. The reason we do this (instead of
2651 * reusing the original function directly) is for better traceability in the Action Scheduler log, in case
2652 * of problems.
2653 *
2654 * @return bool True if there are more records that need to be migrated, false otherwise.
2655 */
2656 function wc_update_722_adjust_new_zealand_states() {
2657 return wc_update_721_adjust_new_zealand_states();
2658 }
2659
2660 /**
2661 * Update the Ukraine state codes in the database after they were updated in code to the CLDR standard.
2662 *
2663 * This is a simple wrapper for the corresponding 7.2.1 update function. The reason we do this (instead of
2664 * reusing the original function directly) is for better traceability in the Action Scheduler log, in case
2665 * of problems.
2666 *
2667 * @return bool True if there are more records that need to be migrated, false otherwise.
2668 */
2669 function wc_update_722_adjust_ukraine_states() {
2670 return wc_update_721_adjust_ukraine_states();
2671 }
2672
2673 /**
2674 * Add new columns date_paid and date_completed to wp_wc_order_stats table in order to provide the option
2675 * of using the dates in the reports
2676 *
2677 * @return void
2678 */
2679 function wc_update_750_add_columns_to_order_stats_table() {
2680 global $wpdb;
2681
2682 $wpdb->query(
2683 "UPDATE {$wpdb->prefix}wc_order_stats AS order_stats
2684 INNER JOIN {$wpdb->postmeta} AS postmeta
2685 ON postmeta.post_id = order_stats.order_id
2686 and postmeta.meta_key = '_date_paid'
2687 SET order_stats.date_paid = IFNULL(FROM_UNIXTIME(postmeta.meta_value), '0000-00-00 00:00:00');"
2688 );
2689
2690 $wpdb->query(
2691 "UPDATE {$wpdb->prefix}wc_order_stats AS order_stats
2692 INNER JOIN {$wpdb->postmeta} AS postmeta
2693 ON postmeta.post_id = order_stats.order_id
2694 and postmeta.meta_key = '_date_completed'
2695 SET order_stats.date_completed = IFNULL(FROM_UNIXTIME(postmeta.meta_value), '0000-00-00 00:00:00');"
2696 );
2697 }
2698
2699 /**
2700 * Disable the experimental product management experience.
2701 *
2702 * @return void
2703 */
2704 function wc_update_750_disable_new_product_management_experience() {
2705 if ( 'yes' === get_option( 'woocommerce_new_product_management_enabled' ) ) {
2706 update_option( 'woocommerce_new_product_management_enabled', 'no' );
2707 }
2708 }
2709
2710 /**
2711 * Remove the multichannel marketing feature flag and options. This feature is now enabled by default.
2712 *
2713 * @return void
2714 */
2715 function wc_update_770_remove_multichannel_marketing_feature_options() {
2716 delete_option( 'woocommerce_multichannel_marketing_enabled' );
2717 delete_option( 'woocommerce_marketing_overview_welcome_hidden' );
2718 }
2719
2720 /**
2721 * Set a flag to indicate whether the blockified Product Grid Block should be used as a template.
2722 *
2723 * @return void
2724 */
2725 function wc_update_790_blockified_product_grid_block() {
2726 update_option( BlockOptions::WC_BLOCK_USE_BLOCKIFIED_PRODUCT_GRID_BLOCK_AS_TEMPLATE, wc_bool_to_string( false ) );
2727 }
2728
2729 /**
2730 * Migrate transaction data which was being incorrectly stored in the postmeta table to HPOS tables.
2731 *
2732 * @return bool Whether there are pending migration records.
2733 */
2734 function wc_update_810_migrate_transactional_metadata_for_hpos() {
2735 global $wpdb;
2736
2737 $data_synchronizer = wc_get_container()->get( DataSynchronizer::class );
2738 if ( ! $data_synchronizer->get_table_exists() ) {
2739 return false;
2740 }
2741
2742 $orders_table = OrdersTableDataStore::get_orders_table_name();
2743 $orders_meta_table = OrdersTableDataStore::get_meta_table_name();
2744
2745 /**
2746 * We are migrating payment_tokens meta that is stored in wp_postmeta table to the HPOS table. To do this with minimum db ops:
2747 * 1. We join postmeta table with wc_orders table directly, this filters out orders that are yet to be migrated and any post with non-order post type.
2748 * 2. A combination of filter on wc_orders_meta.meta_key = _payment_tokens in the join condition itself, along with a null check in a WHERE condition, allows us to only get the orders where the meta is not yet migrated.
2749 */
2750 $select_query = "
2751 SELECT post_id, '_payment_tokens', {$wpdb->postmeta}.meta_value
2752 FROM {$wpdb->postmeta}
2753 JOIN $orders_table ON {$wpdb->postmeta}.post_id = $orders_table.id
2754 LEFT JOIN $orders_meta_table ON $orders_meta_table.order_id = $orders_table.id AND $orders_meta_table.meta_key = '_payment_tokens'
2755 WHERE
2756 {$wpdb->postmeta}.meta_key = '_payment_tokens'
2757 AND $orders_meta_table.order_id IS NULL
2758 ";
2759
2760 // No need to get the data in application, we can insert directly. Sync setting does not matter as this data already exist in the post table. Limit the batch size to 250.
2761 $query =
2762 "
2763 INSERT INTO $orders_meta_table (order_id, meta_key, meta_value)
2764 $select_query
2765 LIMIT 250
2766 ";
2767 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- No user input in the query, everything hardcoded.
2768 $wpdb->query( $query );
2769
2770 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- No user input in the query, everything hardcoded.
2771 $has_pending = $wpdb->query( "$select_query LIMIT 1;" );
2772
2773 return ! empty( $has_pending );
2774 }
2775
2776 /**
2777 * Rename the checkout template to page-checkout.
2778 *
2779 * @return void
2780 */
2781 function wc_update_830_rename_checkout_template() {
2782 $template = get_block_template( BlockTemplateUtils::PLUGIN_SLUG . '//checkout', 'wp_template' );
2783
2784 if ( $template && ! empty( $template->wp_id ) ) {
2785 if ( ! defined( 'WP_POST_REVISIONS' ) ) {
2786 define( 'WP_POST_REVISIONS', false );
2787 }
2788 wp_update_post(
2789 array(
2790 'ID' => $template->wp_id,
2791 'post_name' => 'page-checkout',
2792 )
2793 );
2794 }
2795 }
2796
2797 /**
2798 * Rename the cart template to page-cart.
2799 *
2800 * @return void
2801 */
2802 function wc_update_830_rename_cart_template() {
2803 $template = get_block_template( BlockTemplateUtils::PLUGIN_SLUG . '//cart', 'wp_template' );
2804
2805 if ( $template && ! empty( $template->wp_id ) ) {
2806 if ( ! defined( 'WP_POST_REVISIONS' ) ) {
2807 define( 'WP_POST_REVISIONS', false );
2808 }
2809 wp_update_post(
2810 array(
2811 'ID' => $template->wp_id,
2812 'post_name' => 'page-cart',
2813 )
2814 );
2815 }
2816 }
2817
2818 /**
2819 * Remove the transient data for recommended marketing extensions.
2820 *
2821 * This is removed because it is not used anymore.
2822 * It is replaced by `woocommerce_admin_marketing_recommendations_specs` transient that is created by `MarketingRecommendationsDataSourcePoller`.
2823 *
2824 * @return void
2825 */
2826 function wc_update_860_remove_recommended_marketing_plugins_transient() {
2827 delete_transient( 'wc_marketing_recommended_plugins' );
2828 }
2829
2830 /**
2831 * Create an .htaccess file and an empty index.html file to prevent listing of the default transient files directory,
2832 * if the directory exists.
2833 *
2834 * @return void
2835 */
2836 function wc_update_870_prevent_listing_of_transient_files_directory() {
2837 global $wp_filesystem;
2838
2839 $default_transient_files_dir = untrailingslashit( wp_upload_dir()['basedir'] ) . '/woocommerce_transient_files';
2840 if ( ! is_dir( $default_transient_files_dir ) ) {
2841 return;
2842 }
2843
2844 require_once ABSPATH . 'wp-admin/includes/file.php';
2845 \WP_Filesystem();
2846 $wp_filesystem->put_contents( $default_transient_files_dir . '/.htaccess', 'deny from all' );
2847 $wp_filesystem->put_contents( $default_transient_files_dir . '/index.html', '' );
2848 }
2849
2850 /**
2851 * If it exists, remove the inbox note that asks users to connect to `Woo.com`.
2852 *
2853 * @return void
2854 */
2855 function wc_update_890_update_connect_to_woocommerce_note() {
2856 $note = Notes::get_note_by_name( WooSubscriptionsNotes::CONNECTION_NOTE_NAME );
2857 if ( ! is_a( $note, 'Automattic\WooCommerce\Admin\Notes\Note' ) ) {
2858 return;
2859 }
2860 if ( ! str_contains( $note->get_title(), 'Woo.com' ) ) {
2861 return;
2862 }
2863 if ( $note->get_status() !== Note::E_WC_ADMIN_NOTE_SNOOZED && $note->get_status() !== Note::E_WC_ADMIN_NOTE_UNACTIONED ) {
2864 return;
2865 }
2866 Notes::delete_notes_with_name( WooSubscriptionsNotes::CONNECTION_NOTE_NAME );
2867 }
2868
2869 /**
2870 * Disables the PayPal Standard gateway for stores that aren't using it.
2871 *
2872 * PayPal Standard has been deprecated since WooCommerce 5.5, but there are some stores that have it showing up in their
2873 * list of available Payment methods even if it's not setup. In WooCommerce 8.9 we will disable PayPal Standard for those stores
2874 * to reduce the amount of new connections to the legacy gateway.
2875 *
2876 * Shows an admin notice to inform the store owner that PayPal Standard has been disabled and suggests installing PayPal Payments.
2877 *
2878 * @return void
2879 */
2880 function wc_update_890_update_paypal_standard_load_eligibility() {
2881 $paypal = class_exists( 'WC_Gateway_Paypal' ) ? WC_Gateway_Paypal::get_instance() : null;
2882
2883 if ( ! $paypal ) {
2884 return;
2885 }
2886
2887 // If PayPal is enabled or set to load, but the store hasn't setup PayPal Standard live API keys and doesn't have any PayPal Orders, disable it.
2888 if ( ( 'yes' === $paypal->enabled || 'yes' === $paypal->get_option( '_should_load' ) ) && ! $paypal->get_option( 'api_username' ) && ! $paypal->has_paypal_orders() ) {
2889 $paypal->update_option( '_should_load', wc_bool_to_string( false ) );
2890 }
2891 }
2892
2893 /**
2894 * Create the woocommerce_history_of_autoinstalled_plugins option if it doesn't exist
2895 * as a copy of woocommerce_autoinstalled_plugins if it exists.
2896 *
2897 * @return void
2898 */
2899 function wc_update_891_create_plugin_autoinstall_history_option() {
2900 $autoinstalled_plugins_history_info = get_site_option( 'woocommerce_history_of_autoinstalled_plugins' );
2901 if ( false === $autoinstalled_plugins_history_info ) {
2902 $autoinstalled_plugins_info = get_site_option( 'woocommerce_autoinstalled_plugins' );
2903 if ( false !== $autoinstalled_plugins_info ) {
2904 update_site_option( 'woocommerce_history_of_autoinstalled_plugins', $autoinstalled_plugins_info );
2905 }
2906 }
2907 }
2908
2909 /**
2910 * Add woocommerce_show_lys_tour.
2911 *
2912 * @return void
2913 */
2914 function wc_update_910_add_launch_your_store_tour_option() {
2915 add_option( 'woocommerce_show_lys_tour', 'yes' );
2916 }
2917
2918 /**
2919 * Add woocommerce_hooked_blocks_version option for existing stores that are using a theme that supports the Block Hooks API
2920 *
2921 * @return void
2922 */
2923 function wc_update_920_add_wc_hooked_blocks_version_option() {
2924 if ( ! wp_is_block_theme() && ! current_theme_supports( 'block-template-parts' ) ) {
2925 return;
2926 }
2927
2928 $option_name = 'woocommerce_hooked_blocks_version';
2929 $option_value = get_option( $option_name );
2930
2931 // If the option already exists, we don't need to do anything.
2932 if ( false !== $option_value ) {
2933 return;
2934 }
2935
2936 /**
2937 * A list of theme slugs to execute this with.
2938 * We are applying this filter to allow for the list to be extended by third-parties who were already using it.
2939 *
2940 * @since 8.4.0
2941 */
2942 $theme_include_list = apply_filters( 'woocommerce_hooked_blocks_theme_include_list', array( 'Twenty Twenty-Four', 'Twenty Twenty-Three', 'Twenty Twenty-Two', 'Tsubaki', 'Zaino', 'Thriving Artist', 'Amulet', 'Tazza' ) );
2943 $active_theme_name = wp_get_theme()->get( 'Name' );
2944 $should_set_hooked_blocks_version = in_array( $active_theme_name, $theme_include_list, true );
2945
2946 if ( $should_set_hooked_blocks_version ) {
2947 // Set 8.4.0 as the version for existing stores that are using a theme that supports the Block Hooks API.
2948 // This will ensure that the Block Hooks API is enabled for these stores and works as expected.
2949 // Existing stores that aren't running approved block themes will not have the Block Hooks API enabled.
2950 add_option( $option_name, '8.4.0' );
2951 } else {
2952 // For block themes that aren't approved themes set this option to "no" to completely disable hooked blocks.
2953 // This means we can assume the absence of the option is when a site is switching from a classic theme to a block theme for the first time.
2954 // Note: We have to use "no" instead of false since the latter is the default value for the option if it doesn't exist.
2955 add_option( $option_name, 'no' );
2956 }
2957 }
2958
2959 /**
2960 * Remove user meta associated with the keys '_last_order', '_order_count' and '_money_spent'.
2961 *
2962 * New keys are now used for these, to improve compatibility with multisite networks.
2963 *
2964 * @return void
2965 */
2966 function wc_update_910_remove_obsolete_user_meta() {
2967 global $wpdb;
2968
2969 $deletions = $wpdb->query(
2970 "
2971 DELETE FROM $wpdb->usermeta
2972 WHERE meta_key IN (
2973 '_last_order',
2974 '_order_count',
2975 '_money_spent'
2976 )
2977 "
2978 );
2979
2980 $logger = wc_get_logger();
2981
2982 if ( null === $logger ) {
2983 return;
2984 }
2985
2986 if ( false === $deletions ) {
2987 $logger->notice(
2988 '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.',
2989 array(
2990 'source' => 'wc-updater',
2991 )
2992 );
2993 } else {
2994 $logger->info(
2995 sprintf(
2996 1 === $deletions
2997 ? '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".'
2998 : '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".',
2999 number_format_i18n( $deletions )
3000 ),
3001 array(
3002 'source' => 'wc-updater',
3003 )
3004 );
3005 }
3006 }
3007
3008 /**
3009 * Add woocommerce_coming_soon option when it is not currently present.
3010 *
3011 * @return void
3012 */
3013 function wc_update_930_add_woocommerce_coming_soon_option() {
3014 add_option( 'woocommerce_coming_soon', 'no' );
3015 }
3016
3017 /**
3018 * Migrate Launch Your Store tour meta keys to the woocommerce_meta user data fields.
3019 *
3020 * @return void
3021 */
3022 function wc_update_930_migrate_user_meta_for_launch_your_store_tour() {
3023 // Rename `woocommerce_launch_your_store_tour_hidden` meta key to `woocommerce_admin_launch_your_store_tour_hidden`.
3024 global $wpdb;
3025 $wpdb->query(
3026 $wpdb->prepare(
3027 "UPDATE {$wpdb->usermeta}
3028 SET meta_key = %s
3029 WHERE meta_key = %s",
3030 'woocommerce_admin_launch_your_store_tour_hidden',
3031 'woocommerce_launch_your_store_tour_hidden'
3032 )
3033 );
3034
3035 // Rename `woocommerce_coming_soon_banner_dismissed` meta key to `woocommerce_admin_coming_soon_banner_dismissed`.
3036 $wpdb->query(
3037 $wpdb->prepare(
3038 "UPDATE {$wpdb->usermeta}
3039 SET meta_key = %s
3040 WHERE meta_key = %s",
3041 'woocommerce_admin_coming_soon_banner_dismissed',
3042 'woocommerce_coming_soon_banner_dismissed'
3043 )
3044 );
3045 }
3046
3047 /**
3048 * Recreate FTS index if it already exists, so that phone number can be added to the index.
3049 *
3050 * @return void
3051 */
3052 function wc_update_940_add_phone_to_order_address_fts_index(): void {
3053 $fts_already_exists = get_option( CustomOrdersTableController::HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION ) === 'yes';
3054 if ( ! $fts_already_exists ) {
3055 return;
3056 }
3057
3058 $hpos_controller = wc_get_container()->get( CustomOrdersTableController::class );
3059 $result = $hpos_controller->recreate_order_address_fts_index();
3060 if ( ! $result['status'] ) {
3061 if ( class_exists( 'WC_Admin_Settings ' ) ) {
3062 WC_Admin_Settings::add_error( $result['message'] );
3063 }
3064 }
3065 }
3066
3067 /**
3068 * Remove user meta associated with the key 'woocommerce_admin_help_panel_highlight_shown'.
3069 *
3070 * This key is no longer needed since the help panel spotlight tour has been removed.
3071 *
3072 * @return void
3073 */
3074 function wc_update_940_remove_help_panel_highlight_shown() {
3075 global $wpdb;
3076
3077 $meta_key = 'woocommerce_admin_help_panel_highlight_shown';
3078
3079 $deletions = $wpdb->query(
3080 $wpdb->prepare(
3081 "DELETE FROM $wpdb->usermeta WHERE meta_key = %s",
3082 $meta_key
3083 )
3084 );
3085
3086 // Get the WooCommerce logger to track the results of the deletion.
3087 $logger = wc_get_logger();
3088
3089 if ( null === $logger ) {
3090 return;
3091 }
3092
3093 if ( false === $deletions ) {
3094 $logger->notice(
3095 'During the update to 9.4.0, WooCommerce attempted to remove user meta with the key "woocommerce_admin_help_panel_highlight_shown", but was unable to do so.',
3096 array(
3097 'source' => 'wc-updater',
3098 )
3099 );
3100 } else {
3101 $logger->info(
3102 sprintf(
3103 1 === $deletions
3104 ? 'During the update to 9.4.0, WooCommerce removed %d user meta row associated with the meta key "woocommerce_admin_help_panel_highlight_shown".'
3105 : 'During the update to 9.4.0, WooCommerce removed %d user meta rows associated with the meta key "woocommerce_admin_help_panel_highlight_shown".',
3106 number_format_i18n( $deletions )
3107 ),
3108 array(
3109 'source' => 'wc-updater',
3110 )
3111 );
3112 }
3113 }
3114
3115 /**
3116 * Set multisite customer visibility option for existing sites.
3117 *
3118 * If WooCommerce is updated from an earlier version to 10.0.0, and if it is a multisite network,
3119 * then set 'woocommerce_network_wide_customers' to 'yes' (but only if it has not already been
3120 * set).
3121 *
3122 * This preserves WooCommerce's historic handling of cross-network user visibility for existing
3123 * networks. New sites, or sites that are newly turned into networks at some later point, will
3124 * instead use updated and stricter handling.
3125 *
3126 * @return void
3127 */
3128 function wc_update_1000_multisite_visibility_setting(): void {
3129 if ( ! is_multisite() ) {
3130 return;
3131 }
3132
3133 $existing_site_option = get_site_option( 'woocommerce_network_wide_customers', '' );
3134
3135 if ( is_string( $existing_site_option ) && strlen( $existing_site_option ) > 0 ) {
3136 return;
3137 }
3138
3139 update_site_option( 'woocommerce_network_wide_customers', 'yes' );
3140 }
3141
3142 /**
3143 * Autoloads woocommerce_allow_tracking option.
3144 *
3145 * @return void
3146 */
3147 function wc_update_950_tracking_option_autoload() {
3148 $options = array(
3149 'woocommerce_allow_tracking' => 'yes',
3150 );
3151 wp_set_option_autoload_values( $options );
3152 }
3153
3154 /**
3155 * Update the base color for emails as part of the WooCommerce rebranding,
3156 * but only if the user hasn't specified a custom color.
3157 *
3158 * @return void
3159 */
3160 function wc_update_961_migrate_default_email_base_color() {
3161 $color = get_option( 'woocommerce_email_base_color' );
3162 if ( '#7f54b3' === $color ) {
3163 update_option( 'woocommerce_email_base_color', '#720eec' );
3164 }
3165 }
3166
3167 /**
3168 * Add old refunded order items to the product_lookup_table.
3169 *
3170 * @return void
3171 */
3172 function wc_update_1020_add_old_refunded_order_items_to_product_lookup_table() {
3173 global $wpdb;
3174
3175 // Get every order ID where:
3176 // 1. the total sales is less than 0, and
3177 // 2. is not refunded shipping fee only, and
3178 // 3. is not refunded tax fee only.
3179 $refunded_orders = $wpdb->get_results(
3180 "SELECT order_stats.order_id, order_stats.num_items_sold
3181 FROM {$wpdb->prefix}wc_order_stats AS order_stats
3182 WHERE order_stats.total_sales < 0 # Refunded orders
3183 AND order_stats.total_sales != order_stats.shipping_total # Exclude refunded orders that only include a shipping refund
3184 AND order_stats.total_sales != order_stats.tax_total # Exclude refunded orders that only include a tax refund"
3185 );
3186
3187 if ( $refunded_orders ) {
3188 update_option( 'woocommerce_analytics_uses_old_full_refund_data', 'yes' );
3189 foreach ( $refunded_orders as $refunded_order ) {
3190 if ( intval( $refunded_order->num_items_sold ) === 0 ) {
3191 $order = wc_get_order( $refunded_order->order_id );
3192 if ( ! $order ) {
3193 continue;
3194 }
3195 // If the refund order has no line items, mark it as a full refund in orders_meta table.
3196 // In the above query we already excluded orders for refunded shipping and tax, so it's safe to assume that the refund order without items is a full refund.
3197 // Note that the "full" refund here means it's created by changing the order status to "Refunded", not partially refund all the items in the order.
3198 if ( empty( $order->get_items() ) ) {
3199 wc_get_logger()->info( sprintf( 'Setting refund type to full for order_id: %s', $refunded_order->order_id ) );
3200 $order->update_meta_data( '_refund_type', 'full' );
3201 $order->save_meta_data();
3202 }
3203 }
3204 }
3205 }
3206 }
3207
3208 /**
3209 * Remove the option woocommerce_order_attribution_install_banner_dismissed.
3210 * This data is now stored in the user meta table in the PR #55715.
3211 *
3212 * @return void
3213 */
3214 function wc_update_980_remove_order_attribution_install_banner_dismissed_option() {
3215 delete_option( 'woocommerce_order_attribution_install_banner_dismissed' );
3216 }
3217
3218 /**
3219 * One-time force enable the new Payments Settings page feature for all stores.
3220 *
3221 * @return void
3222 */
3223 function wc_update_985_enable_new_payments_settings_page_feature() {
3224 update_option( 'woocommerce_feature_reactify-classic-payments-settings_enabled', 'yes' );
3225 }
3226
3227 /**
3228 * Remove the transient wc_count_comments as this has migrated to use cache.
3229 *
3230 * @return void
3231 */
3232 function wc_update_990_remove_wc_count_comments_transient() {
3233 delete_transient( 'wc_count_comments' );
3234 }
3235
3236 /**
3237 * Remove all notes of type 'email' from wp_wc_admin_notes table.
3238 *
3239 * @return void
3240 */
3241 function wc_update_990_remove_email_notes() {
3242 global $wpdb;
3243
3244 $wpdb->delete(
3245 $wpdb->prefix . 'wc_admin_notes',
3246 array(
3247 'type' => 'email',
3248 ),
3249 array( '%s' )
3250 );
3251 }
3252
3253 /**
3254 * Remove the transient ptk_patterns.
3255 * This was used to store the Patterns Toolkit patterns in the database.
3256 * The patterns are now stored in the option ptk_patterns.
3257 *
3258 * @return void
3259 */
3260 function wc_update_1000_remove_patterns_toolkit_transient() {
3261 delete_transient( 'ptk_patterns' );
3262 }
3263
3264 /**
3265 * Add an index to (comment_date_gmt, comment_type, comment_approved, comment_post_ID)
3266 * on the comments table to improve the admin query that gets the latest 25 comments
3267 * while excluding reviews and internal notes.
3268 *
3269 * @return void
3270 */
3271 function wc_update_1030_add_comments_date_type_index() {
3272 global $wpdb;
3273 $date_type_index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE key_name = 'woo_idx_comment_date_type'" );
3274
3275 if ( is_null( $date_type_index_exists ) ) {
3276 // Improve performance of the admin comments query when fetching the latest 25 comments while excluding reviews and internal notes.
3277 $wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_date_type (comment_date_gmt, comment_type, comment_approved, comment_post_ID)" );
3278 }
3279 }
3280
3281 /**
3282 * Clean up the old last_fetch_patterns_request option and non-grouped actions after migration to using the `action_scheduler_ensure_recurring_actions` hook.
3283 *
3284 * In version 10.4.0, this functionality was replaced with Action Scheduler recurring actions.
3285 *
3286 * @return void
3287 */
3288 function wc_update_1040_cleanup_legacy_ptk_patterns_fetching() {
3289 delete_option( 'last_fetch_patterns_request' );
3290 as_unschedule_all_actions( 'fetch_patterns' );
3291 }
3292
3293 /**
3294 * Update brand permalink setting to take into account obsolete 'woocommerce_prepend_shop_page_to_urls' option, removed in WC 2.0.3.
3295 * This migration ensures any installations that still have this old option set will have their brand permalink updated appropriately.
3296 *
3297 * @since 10.5.0
3298 *
3299 * @return void
3300 */
3301 function wc_update_1050_migrate_brand_permalink_setting() {
3302 if ( 'yes' !== get_option( 'woocommerce_prepend_shop_page_to_urls' ) ) {
3303 return;
3304 }
3305
3306 $shop_page_id = wc_get_page_id( 'shop' );
3307 $shop_slug = ( $shop_page_id > 0 && get_post( $shop_page_id ) ) ? get_page_uri( $shop_page_id ) : 'shop';
3308
3309 if ( ! $shop_slug ) {
3310 return;
3311 }
3312
3313 $slug = trailingslashit( $shop_slug ) . __( 'brand', 'woocommerce' );
3314 update_option( 'woocommerce_brand_permalink', $slug );
3315 }
3316
3317 /**
3318 * Autoload frequently used options for performance improvements (see https://github.com/woocommerce/woocommerce/issues/61855)
3319 *
3320 * `$autoload_options` are frequently used options that may already be in the db but with `autoload = off`.
3321 * `$feature_options` are frequently used feature flag options that are not stored in the db.
3322 *
3323 * @return void
3324 */
3325 function wc_update_1050_enable_autoload_options() {
3326 global $wpdb;
3327
3328 $autoload_options = array(
3329 // Page ID options with autoload `off` in the db.
3330 'woocommerce_myaccount_page_id',
3331 'woocommerce_cart_page_id',
3332 'woocommerce_checkout_page_id',
3333 'woocommerce_terms_page_id',
3334 // Feature status options with autoload `off` in the db.
3335 'woocommerce_show_marketplace_suggestions',
3336 'woocommerce_enable_delayed_account_creation',
3337 'wc_feature_woocommerce_brands_enabled',
3338 'wc_connect_taxes_enabled',
3339 'woocommerce_logs_logging_enabled',
3340 'woocommerce_email_improvements_existing_store_enabled',
3341 'woocommerce_custom_orders_table_data_sync_enabled',
3342 );
3343
3344 $feature_options = array(
3345 'fulfillments' => 'woocommerce_feature_fulfillments_enabled',
3346 'push_notifications' => 'woocommerce_feature_push_notifications_enabled',
3347 'agentic_checkout' => 'woocommerce_feature_agentic_checkout_enabled',
3348 'cart_checkout_blocks' => 'woocommerce_feature_cart_checkout_blocks_enabled',
3349 );
3350
3351 $features_controller = wc_get_container()->get( FeaturesController::class );
3352
3353 foreach ( $feature_options as $key => $option ) {
3354 if ( false === get_option( $option, false ) ) {
3355 add_option( $option, wc_bool_to_string( $features_controller->feature_is_enabled( $key ) ), '', true );
3356 } else {
3357 $autoload_options[] = $option;
3358 }
3359 }
3360
3361 $placeholders = implode( ', ', array_fill( 0, count( $autoload_options ), '%s' ) );
3362
3363 $wpdb->query(
3364 $wpdb->prepare(
3365 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
3366 "UPDATE {$wpdb->options} SET autoload = 'on' WHERE option_name IN ($placeholders)",
3367 ...$autoload_options
3368 )
3369 );
3370 }
3371
3372 /**
3373 * Remove deprecated marketplace feature option from the database.
3374 *
3375 * The marketplace feature flag was deprecated in 10.5.0 and is now always enabled.
3376 * The option is no longer needed as FeaturesUtil::feature_is_enabled('marketplace')
3377 * returns the deprecated_value directly without reading from the database.
3378 *
3379 * @since 10.5.0
3380 *
3381 * @return void
3382 */
3383 function wc_update_1050_remove_deprecated_marketplace_option(): void {
3384 delete_option( 'woocommerce_feature_marketplace_enabled' );
3385 }
3386
3387 /**
3388 * Add the `woo_idx_comment_approved_type` index to improve the performance of comment-related queries in the admin area.
3389 *
3390 * @since 10.6.0
3391 *
3392 * @return void
3393 */
3394 function wc_update_1060_add_woo_idx_comment_approved_type_index(): void {
3395 global $wpdb;
3396
3397 $comment_approved_type_index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE key_name = 'woo_idx_comment_approved_type'" );
3398 if ( null === $comment_approved_type_index_exists ) {
3399 // Improve performance of the admin comments query when counting approved comments while excluding internal notes.
3400 $wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_approved_type (comment_approved, comment_type, comment_post_ID)" );
3401 }
3402 }
3403
3404 /**
3405 * Add an admin notice about HPOS sync-on-read being disabled by default for sites
3406 * that have both HPOS and data synchronization enabled.
3407 *
3408 * @since 10.7.0
3409 *
3410 * @return void
3411 */
3412 function wc_update_1070_disable_hpos_sync_on_read(): void {
3413 if ( 'yes' !== get_option( 'woocommerce_custom_orders_table_enabled' ) ) {
3414 return;
3415 }
3416
3417 if ( 'yes' !== get_option( 'woocommerce_custom_orders_table_data_sync_enabled' ) ) {
3418 return;
3419 }
3420
3421 WC_Admin_Notices::add_notice( 'hpos_sync_on_read_disabled' );
3422 }
3423
3424 /**
3425 * Migrate the legacy analytics immediate import option to the renamed scheduled import option.
3426 *
3427 * In 10.5.0, `woocommerce_analytics_immediate_import` was renamed to
3428 * `woocommerce_analytics_scheduled_import` with inverted semantics, but no
3429 * migration was added. Stores that had opted into scheduled imports (legacy
3430 * value 'no') silently reverted to immediate imports on upgrade.
3431 *
3432 * @since 10.8.0
3433 *
3434 * @return void
3435 */
3436 function wc_update_1080_migrate_analytics_import_option(): void {
3437 $legacy_option = 'woocommerce_analytics_immediate_import';
3438 $new_option = 'woocommerce_analytics_scheduled_import';
3439
3440 $legacy_value = get_option( $legacy_option, false );
3441
3442 // Nothing to migrate if the legacy option was never set.
3443 if ( false === $legacy_value ) {
3444 return;
3445 }
3446
3447 // If the new option already exists, just clean up the legacy option.
3448 if ( false !== get_option( $new_option, false ) ) {
3449 delete_option( $legacy_option );
3450 return;
3451 }
3452
3453 // Invert the semantics: legacy 'yes' (immediate) = new 'no' (not scheduled),
3454 // legacy 'no' (not immediate) = new 'yes' (scheduled).
3455 $new_value = 'no' === $legacy_value ? 'yes' : 'no';
3456
3457 // Only delete the legacy option if the new option was written successfully.
3458 // On failure, the runtime fallback in is_scheduled_import_enabled() can
3459 // still read the legacy option to preserve the store's preference.
3460 if ( add_option( $new_option, $new_value ) ) {
3461 delete_option( $legacy_option );
3462 }
3463 }
3464
3465 /**
3466 * Reshape the `meta_key_value` index on `wc_orders_meta` to `(meta_key(50), meta_value(20))`.
3467 *
3468 * This is labeled as a 10.8.0-2 migration because originally there was a 10.8 migration that modified the
3469 * meta_key_value index to include only the meta_key column (see https://github.com/woocommerce/woocommerce/pull/63897),
3470 * but later it was discovered that this caused performance issues (as commented in the same pull request),
3471 * so this new migration was added. However sites where 10.8 beta was installed will already have run
3472 * the original migration (the one that removed meta_value from the index) and will have their db
3473 * version updated to 10.8, so this new migration wouldn't run. Hence the 10.8.0-2 marker, which
3474 * sorts between 10.8.0 and 10.8.1 via version_compare() and leaves the 10.8.1 slot free for any
3475 * future patch-release migrations. WooCommerce 10.8.0 beta 2 had a migration labeled as 10.8.0-1 already,
3476 * hence the need to go with 10.8.0-2 for this one.
3477 *
3478 * Handles two starting states transparently:
3479 * - Pre-10.8 sites: the index is the original `(meta_key(100), meta_value(82))`.
3480 * - 10.8 beta sites: the index has been changed to `(meta_key(100))` only.
3481 *
3482 * The new prefixes are sized from profiling a production-scale table: all meta_keys fit
3483 * within 47 chars (so `meta_key(50)` covers every existing key) and a 20-byte `meta_value`
3484 * prefix preserves full selectivity for the affected query patterns, keeping most of the
3485 * storage benefit of the slim attempt.
3486 *
3487 * @since 10.8.0
3488 *
3489 * @return void
3490 */
3491 function wc_update_10802_restore_orders_meta_key_value_index(): void {
3492 global $wpdb;
3493
3494 $table_name = $wpdb->prefix . 'wc_orders_meta';
3495 $index_name = 'meta_key_value';
3496
3497 // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
3498 $columns = $wpdb->get_results(
3499 $wpdb->prepare(
3500 'SHOW INDEX FROM ' . $table_name . ' WHERE Key_name = %s',
3501 $index_name
3502 )
3503 );
3504 // phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
3505
3506 $already_correct =
3507 is_array( $columns ) && 2 === count( $columns ) &&
3508 'meta_key' === $columns[0]->Column_name && 50 === (int) $columns[0]->Sub_part &&
3509 'meta_value' === $columns[1]->Column_name && 20 === (int) $columns[1]->Sub_part;
3510
3511 if ( $already_correct ) {
3512 return;
3513 }
3514
3515 if ( empty( $columns ) ) {
3516 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
3517 $wpdb->query( "ALTER TABLE {$table_name} ADD INDEX {$index_name} (meta_key(50), meta_value(20))" );
3518 } else {
3519 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
3520 $wpdb->query( "ALTER TABLE {$table_name} DROP INDEX {$index_name}, ADD INDEX {$index_name} (meta_key(50), meta_value(20))" );
3521 }
3522 }
3523
3524 /**
3525 * Backfill sync meta onto pre-existing `woo_email` posts so the template
3526 * divergence detector introduced in the same release (RSM-138) can classify
3527 * legacy installs safely.
3528 *
3529 * @since 10.8.0
3530 *
3531 * @return bool Always false. Once-per-site is enforced by the
3532 * `woocommerce_db_version` fence in `$db_updates`.
3533 */
3534 function wc_update_1080_backfill_email_template_sync_meta(): bool {
3535 return WCEmailTemplateSyncBackfill::run();
3536 }
3537
3538 /**
3539 * Remove the option woocommerce_task_list_reminder_bar_hidden.
3540 * The task list reminder bar was removed in 10.9.0; this option is no longer used.
3541 *
3542 * @since 10.9.0
3543 *
3544 * @return void
3545 */
3546 function wc_update_1090_remove_task_list_reminder_bar_hidden_option() {
3547 delete_option( 'woocommerce_task_list_reminder_bar_hidden' );
3548 }
3549
3550 /**
3551 * Remove the deprecated push_notifications feature option from the database.
3552 *
3553 * The push_notifications feature flag was deprecated in 10.9.2 and is now always enabled.
3554 * The option is no longer needed as FeaturesUtil::feature_is_enabled('push_notifications')
3555 * returns the deprecated_value directly without reading from the database. Removing it also
3556 * clears the stale "no" value that wc_update_1050_enable_autoload_options() persisted for
3557 * stores upgrading across the 10.5.0 boundary.
3558 *
3559 * @since 10.9.2
3560 *
3561 * @return void
3562 */
3563 function wc_update_10902_remove_deprecated_push_notifications_option(): void {
3564 delete_option( 'woocommerce_feature_push_notifications_enabled' );
3565 }
3566