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