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