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 / class-wc-install.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 2 days 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 2 days 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 1 month 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 2 days 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
class-wc-install.php
3295 lines
1 <?php
2 /**
3 * Installation related functions and actions.
4 *
5 * @package WooCommerce\Classes
6 * @version x.x.x
7 */
8
9 use Automattic\Jetpack\Constants;
10 use Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\DataStore;
11 use Automattic\WooCommerce\Enums\ProductType;
12 use Automattic\WooCommerce\Internal\Admin\EmailImprovements\EmailImprovements;
13 use Automattic\WooCommerce\Internal\Caches\ProductCacheController;
14 use Automattic\WooCommerce\Internal\TransientFiles\TransientFilesEngine;
15 use Automattic\WooCommerce\Internal\DataStores\Orders\{ CustomOrdersTableController, DataSynchronizer, OrdersTableDataStore };
16 use Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificationsDataStore;
17 use Automattic\WooCommerce\Internal\Features\FeaturesController;
18 use Automattic\WooCommerce\Internal\ProductAttributesLookup\DataRegenerator;
19 use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Synchronize as Download_Directories_Sync;
20 use Automattic\WooCommerce\Admin\API\Reports\Orders\Stats\DataStore as OrdersStatsDataStore;
21 use Automattic\WooCommerce\Utilities\FeaturesUtil;
22 use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
23 use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper as WCConnectionHelper;
24 use Automattic\WooCommerce\Internal\Utilities\ProductUtil;
25 use Automattic\WooCommerce\Utilities\{ OrderUtil, PluginUtil };
26
27 defined( 'ABSPATH' ) || exit;
28
29 /**
30 * WC_Install Class.
31 */
32 class WC_Install {
33 /**
34 * DB updates and callbacks that need to be run per version.
35 *
36 * Please note that these functions are invoked when WooCommerce is updated from a previous version,
37 * but NOT when WooCommerce is newly installed.
38 *
39 * Database schema changes must be incorporated to the SQL returned by get_schema, which is applied
40 * via dbDelta at both install and update time. If any other kind of database change is required
41 * at install time (e.g. populating tables), use the 'woocommerce_installed' hook.
42 *
43 * IMPORTANT:
44 * When adding new update callbacks after feature freeze, always use a unique version key with a suffix (e.g. `10.2.0-1`)
45 * if the base version already exists in the array.
46 * This ensures all users, including those on beta or RC versions, receive the update.
47 *
48 * @var array
49 */
50 private static $db_updates = array(
51 '2.0.0' => array(
52 'wc_update_200_file_paths',
53 'wc_update_200_permalinks',
54 'wc_update_200_subcat_display',
55 'wc_update_200_taxrates',
56 'wc_update_200_line_items',
57 'wc_update_200_images',
58 'wc_update_200_db_version',
59 ),
60 '2.0.9' => array(
61 'wc_update_209_brazillian_state',
62 'wc_update_209_db_version',
63 ),
64 '2.1.0' => array(
65 'wc_update_210_remove_pages',
66 'wc_update_210_file_paths',
67 'wc_update_210_db_version',
68 ),
69 '2.2.0' => array(
70 'wc_update_220_shipping',
71 'wc_update_220_order_status',
72 'wc_update_220_variations',
73 'wc_update_220_attributes',
74 'wc_update_220_db_version',
75 ),
76 '2.3.0' => array(
77 'wc_update_230_options',
78 'wc_update_230_db_version',
79 ),
80 '2.4.0' => array(
81 'wc_update_240_options',
82 'wc_update_240_shipping_methods',
83 'wc_update_240_api_keys',
84 'wc_update_240_refunds',
85 'wc_update_240_db_version',
86 ),
87 '2.4.1' => array(
88 'wc_update_241_variations',
89 'wc_update_241_db_version',
90 ),
91 '2.5.0' => array(
92 'wc_update_250_currency',
93 'wc_update_250_db_version',
94 ),
95 '2.6.0' => array(
96 'wc_update_260_options',
97 'wc_update_260_termmeta',
98 'wc_update_260_zones',
99 'wc_update_260_zone_methods',
100 'wc_update_260_refunds',
101 'wc_update_260_db_version',
102 ),
103 '3.0.0' => array(
104 'wc_update_300_grouped_products',
105 'wc_update_300_settings',
106 'wc_update_300_product_visibility',
107 'wc_update_300_db_version',
108 ),
109 '3.1.0' => array(
110 'wc_update_310_downloadable_products',
111 'wc_update_310_old_comments',
112 'wc_update_310_db_version',
113 ),
114 '3.1.2' => array(
115 'wc_update_312_shop_manager_capabilities',
116 'wc_update_312_db_version',
117 ),
118 '3.2.0' => array(
119 'wc_update_320_mexican_states',
120 'wc_update_320_db_version',
121 ),
122 '3.3.0' => array(
123 'wc_update_330_image_options',
124 'wc_update_330_webhooks',
125 'wc_update_330_product_stock_status',
126 'wc_update_330_set_default_product_cat',
127 'wc_update_330_clear_transients',
128 'wc_update_330_set_paypal_sandbox_credentials',
129 'wc_update_330_db_version',
130 ),
131 '3.4.0' => array(
132 'wc_update_340_states',
133 'wc_update_340_state',
134 'wc_update_340_last_active',
135 'wc_update_340_db_version',
136 ),
137 '3.4.3' => array(
138 'wc_update_343_cleanup_foreign_keys',
139 'wc_update_343_db_version',
140 ),
141 '3.4.4' => array(
142 'wc_update_344_recreate_roles',
143 'wc_update_344_db_version',
144 ),
145 '3.5.0' => array(
146 'wc_update_350_reviews_comment_type',
147 'wc_update_350_db_version',
148 ),
149 '3.5.2' => array(
150 'wc_update_352_drop_download_log_fk',
151 ),
152 '3.5.4' => array(
153 'wc_update_354_modify_shop_manager_caps',
154 'wc_update_354_db_version',
155 ),
156 '3.6.0' => array(
157 'wc_update_360_product_lookup_tables',
158 'wc_update_360_term_meta',
159 'wc_update_360_downloadable_product_permissions_index',
160 'wc_update_360_db_version',
161 ),
162 '3.7.0' => array(
163 'wc_update_370_tax_rate_classes',
164 'wc_update_370_mro_std_currency',
165 'wc_update_370_db_version',
166 ),
167 '3.9.0' => array(
168 'wc_update_390_move_maxmind_database',
169 'wc_update_390_change_geolocation_database_update_cron',
170 'wc_update_390_db_version',
171 ),
172 '4.0.0' => array(
173 'wc_update_product_lookup_tables',
174 'wc_update_400_increase_size_of_column',
175 'wc_update_400_reset_action_scheduler_migration_status',
176 'wc_admin_update_0201_order_status_index',
177 'wc_admin_update_0230_rename_gross_total',
178 'wc_admin_update_0251_remove_unsnooze_action',
179 'wc_update_400_db_version',
180 ),
181 '4.4.0' => array(
182 'wc_update_440_insert_attribute_terms_for_variable_products',
183 'wc_admin_update_110_remove_facebook_note',
184 'wc_admin_update_130_remove_dismiss_action_from_tracking_opt_in_note',
185 'wc_update_440_db_version',
186 ),
187 '4.5.0' => array(
188 'wc_update_450_sanitize_coupons_code',
189 'wc_update_450_db_version',
190 ),
191 '5.0.0' => array(
192 'wc_update_500_fix_product_review_count',
193 'wc_admin_update_160_remove_facebook_note',
194 'wc_admin_update_170_homescreen_layout',
195 'wc_update_500_db_version',
196 ),
197 '5.6.0' => array(
198 'wc_update_560_create_refund_returns_page',
199 'wc_update_560_db_version',
200 ),
201 '6.0.0' => array(
202 'wc_update_600_migrate_rate_limit_options',
203 'wc_admin_update_270_delete_report_downloads',
204 'wc_admin_update_271_update_task_list_options',
205 'wc_admin_update_280_order_status',
206 'wc_admin_update_290_update_apperance_task_option',
207 'wc_admin_update_290_delete_default_homepage_layout_option',
208 'wc_update_600_db_version',
209 ),
210 '6.3.0' => array(
211 'wc_update_630_create_product_attributes_lookup_table',
212 'wc_admin_update_300_update_is_read_from_last_read',
213 'wc_update_630_db_version',
214 ),
215 '6.4.0' => array(
216 'wc_update_640_add_primary_key_to_product_attributes_lookup_table',
217 'wc_admin_update_340_remove_is_primary_from_note_action',
218 'wc_update_640_db_version',
219 ),
220 '6.5.0' => array(
221 'wc_update_650_approved_download_directories',
222 ),
223 '6.5.1' => array(
224 'wc_update_651_approved_download_directories',
225 ),
226 '6.7.0' => array(
227 'wc_update_670_purge_comments_count_cache',
228 'wc_update_670_delete_deprecated_remote_inbox_notifications_option',
229 ),
230 '7.0.0' => array(
231 'wc_update_700_remove_download_log_fk',
232 'wc_update_700_remove_recommended_marketing_plugins_transient',
233 ),
234 '7.2.1' => array(
235 'wc_update_721_adjust_new_zealand_states',
236 'wc_update_721_adjust_ukraine_states',
237 ),
238 '7.2.2' => array(
239 'wc_update_722_adjust_new_zealand_states',
240 'wc_update_722_adjust_ukraine_states',
241 ),
242 '7.5.0' => array(
243 'wc_update_750_add_columns_to_order_stats_table',
244 'wc_update_750_disable_new_product_management_experience',
245 ),
246 '7.7.0' => array(
247 'wc_update_770_remove_multichannel_marketing_feature_options',
248 ),
249 '7.9.0' => array(
250 'wc_update_790_blockified_product_grid_block',
251 ),
252 '8.1.0' => array(
253 'wc_update_810_migrate_transactional_metadata_for_hpos',
254 ),
255 '8.3.0' => array(
256 'wc_update_830_rename_checkout_template',
257 'wc_update_830_rename_cart_template',
258 ),
259 '8.6.0' => array(
260 'wc_update_860_remove_recommended_marketing_plugins_transient',
261 ),
262 '8.7.0' => array(
263 'wc_update_870_prevent_listing_of_transient_files_directory',
264 ),
265 '8.9.0' => array(
266 'wc_update_890_update_connect_to_woocommerce_note',
267 'wc_update_890_update_paypal_standard_load_eligibility',
268 ),
269 '8.9.1' => array(
270 'wc_update_891_create_plugin_autoinstall_history_option',
271 ),
272 '9.1.0' => array(
273 'wc_update_910_add_launch_your_store_tour_option',
274 'wc_update_910_remove_obsolete_user_meta',
275 ),
276 '9.2.0' => array(
277 'wc_update_920_add_wc_hooked_blocks_version_option',
278 ),
279 '9.3.0' => array(
280 'wc_update_930_add_woocommerce_coming_soon_option',
281 'wc_update_930_migrate_user_meta_for_launch_your_store_tour',
282 ),
283 '9.4.0' => array(
284 'wc_update_940_add_phone_to_order_address_fts_index',
285 'wc_update_940_remove_help_panel_highlight_shown',
286 ),
287 '9.5.0' => array(
288 'wc_update_950_tracking_option_autoload',
289 ),
290 '9.6.1' => array(
291 'wc_update_961_migrate_default_email_base_color',
292 ),
293 '9.8.0' => array(
294 'wc_update_980_remove_order_attribution_install_banner_dismissed_option',
295 ),
296 '9.8.5' => array(
297 'wc_update_985_enable_new_payments_settings_page_feature',
298 ),
299 '9.9.0' => array(
300 'wc_update_990_remove_wc_count_comments_transient',
301 'wc_update_990_remove_email_notes',
302 ),
303 '10.0.0' => array(
304 'wc_update_1000_multisite_visibility_setting',
305 'wc_update_1000_remove_patterns_toolkit_transient',
306 ),
307 '10.2.0' => array(
308 'wc_update_1020_add_old_refunded_order_items_to_product_lookup_table',
309 ),
310 '10.3.0' => array(
311 'wc_update_1030_add_comments_date_type_index',
312 ),
313 '10.4.0' => array(
314 'wc_update_1040_add_idx_date_paid_status_parent',
315 'wc_update_1040_cleanup_legacy_ptk_patterns_fetching',
316 ),
317 '10.5.0' => array(
318 'wc_update_1050_migrate_brand_permalink_setting',
319 'wc_update_1050_enable_autoload_options',
320 'wc_update_1050_add_idx_user_email',
321 'wc_update_1050_remove_deprecated_marketplace_option',
322 ),
323 '10.6.0' => array(
324 'wc_update_1060_add_woo_idx_comment_approved_type_index',
325 ),
326 '10.7.0' => array(
327 'wc_update_1070_disable_hpos_sync_on_read',
328 ),
329 '10.8.0' => array(
330 'wc_update_1080_migrate_analytics_import_option',
331 'wc_update_1080_backfill_email_template_sync_meta',
332 ),
333 '10.8.0-2' => array(
334 'wc_update_10802_restore_orders_meta_key_value_index',
335 ),
336 '10.9.0' => array(
337 'wc_update_1090_remove_task_list_reminder_bar_hidden_option',
338 ),
339 '10.9.2' => array(
340 'wc_update_10902_remove_deprecated_push_notifications_option',
341 ),
342 '11.0.0' => array(
343 'wc_update_1100_enable_point_of_sale_feature',
344 ),
345 '11.1.0' => array(
346 'wc_update_1110_delete_dashboard_outofstock_count_transient',
347 'wc_update_1110_cleanup_block_email_posts',
348 'wc_update_1110_flush_product_count_cache',
349 ),
350 '11.1.0-1' => array(
351 'wc_update_11101_remove_deprecated_variation_gallery_option',
352 ),
353 );
354
355 /**
356 * Option name used to track new installations of WooCommerce.
357 *
358 * @var string
359 */
360 const NEWLY_INSTALLED_OPTION = 'woocommerce_newly_installed';
361
362 /**
363 * Option name used to track new installation versions of WooCommerce.
364 *
365 * @var string
366 */
367 const INITIAL_INSTALLED_VERSION = 'woocommerce_initial_installed_version';
368
369 /**
370 * Option name used to uniquely identify installations of WooCommerce.
371 *
372 * @var string
373 */
374 const STORE_ID_OPTION = 'woocommerce_store_id';
375
376 /**
377 * Hook in tabs.
378 *
379 * @return void
380 */
381 public static function init() {
382 if ( ! empty( $GLOBALS['wc_uninstalling_plugin'] ) ) {
383 return;
384 }
385
386 add_action( 'init', array( __CLASS__, 'check_version' ), 5 );
387 add_action( 'init', array( __CLASS__, 'manual_database_update' ), 20 );
388 add_action( 'woocommerce_newly_installed', array( __CLASS__, 'maybe_enable_hpos' ), 20 );
389 add_action( 'woocommerce_newly_installed', array( __CLASS__, 'add_coming_soon_option' ), 20 );
390 add_action( 'woocommerce_newly_installed', array( __CLASS__, 'enable_email_improvements_for_newly_installed' ), 20 );
391 add_action( 'woocommerce_newly_installed', array( __CLASS__, 'enable_customer_stock_notifications_signups' ), 20 );
392 add_action( 'woocommerce_newly_installed', array( __CLASS__, 'enable_analytics_scheduled_import' ), 20 );
393 add_action( 'woocommerce_newly_installed', array( __CLASS__, 'enable_product_instance_caching_for_newly_installed' ), 20 );
394 add_action( 'woocommerce_updated', array( __CLASS__, 'enable_email_improvements_for_existing_merchants' ), 20 );
395 add_action( 'woocommerce_run_update_callback', array( __CLASS__, 'run_update_callback' ) );
396 add_action( 'woocommerce_update_db_to_current_version', array( __CLASS__, 'update_db_version' ) );
397 add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
398 add_action( 'woocommerce_page_created', array( __CLASS__, 'page_created' ), 10, 2 );
399 add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
400 add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
401 add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
402 add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
403 add_action( 'admin_init', array( __CLASS__, 'newly_installed' ) );
404
405 // DB update notice.
406 add_filter( 'woocommerce_get_note_from_db', array( \WC_Notes_Run_Db_Update::class, 'maybe_update_notice' ), 10 );
407 add_action( 'woocommerce_hide_update_notice', array( __CLASS__, 'remove_update_db_notice' ) );
408 }
409
410 /**
411 * Trigger `woocommerce_newly_installed` action for new installations.
412 *
413 * @since 8.0.0
414 *
415 * @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
416 *
417 * @return void
418 */
419 public static function newly_installed() {
420 if ( 'yes' === get_option( self::NEWLY_INSTALLED_OPTION, false ) ) {
421 /**
422 * Run when WooCommerce has been installed for the first time.
423 *
424 * @since 6.5.0
425 */
426 do_action( 'woocommerce_newly_installed' );
427 do_action_deprecated( 'woocommerce_admin_newly_installed', array(), '6.5.0', 'woocommerce_newly_installed' );
428
429 update_option( self::NEWLY_INSTALLED_OPTION, 'no' );
430
431 /**
432 * This option is used to track the initial version of WooCommerce that was installed.
433 *
434 * @since 9.2.0
435 */
436 add_option( self::INITIAL_INSTALLED_VERSION, WC()->version, '', false );
437 }
438 }
439
440 /**
441 * Check WooCommerce version and run the updater is required.
442 *
443 * This check is done on all requests and runs if the versions do not match.
444 *
445 * @return void
446 */
447 public static function check_version() {
448 $wc_version = get_option( 'woocommerce_version' );
449 $wc_code_version = WC()->version;
450 $requires_update = version_compare( $wc_version, $wc_code_version, '<' );
451 if ( ! Constants::is_defined( 'IFRAME_REQUEST' ) && $requires_update ) {
452 self::install();
453 /**
454 * Run after WooCommerce has been updated.
455 *
456 * @since 2.2.0
457 */
458 do_action( 'woocommerce_updated' );
459 do_action_deprecated( 'woocommerce_admin_updated', array(), $wc_code_version, 'woocommerce_updated' );
460 }
461 }
462
463 /**
464 * Performan manual database update when triggered by WooCommerce System Tools.
465 *
466 * @since 3.6.5
467 *
468 * @return void
469 */
470 public static function manual_database_update() {
471 $blog_id = get_current_blog_id();
472
473 add_action( 'wp_' . $blog_id . '_wc_updater_cron', array( __CLASS__, 'run_manual_database_update' ) );
474 }
475
476 /**
477 * Add WC Admin based db update notice.
478 *
479 * @since 4.0.0
480 * @deprecated 10.3.0
481 *
482 * @return void
483 */
484 public static function wc_admin_db_update_notice() {
485 if (
486 WC()->is_wc_admin_active()
487 && false !== get_option( 'woocommerce_admin_install_timestamp' )
488 ) {
489 new WC_Notes_Run_Db_Update();
490 }
491 }
492
493 /**
494 * Adds the db update notice.
495 *
496 * @since 10.3.0
497 *
498 * @return void
499 */
500 private static function add_update_db_notice() {
501 if ( ! \WC_Admin_Notices::has_notice( 'update' ) ) {
502 \WC_Admin_Notices::add_notice( 'update', true );
503 }
504
505 try {
506 if ( WC()->is_wc_admin_active() && false !== get_option( 'woocommerce_admin_install_timestamp' ) ) {
507 \WC_Notes_Run_Db_Update::add_notice();
508 }
509 } catch ( Exception $e ) {
510 wc_get_logger()->error( 'Error adding db update note: ' . $e->getMessage(), array( 'source' => 'wc-updater' ) );
511 }
512 }
513
514 /**
515 * Removes the db update notice.
516 *
517 * @since 10.3.0
518 *
519 * @return void
520 */
521 public static function remove_update_db_notice() {
522 if ( \WC_Admin_Notices::has_notice( 'update' ) ) {
523 \WC_Admin_Notices::remove_notice( 'update', true );
524 }
525
526 try {
527 if ( WC()->is_wc_admin_active() && false !== get_option( 'woocommerce_admin_install_timestamp' ) ) {
528 \WC_Notes_Run_Db_Update::set_notice_actioned();
529 }
530 } catch ( Exception $e ) {
531 wc_get_logger()->error( 'Error removing db update note: ' . $e->getMessage(), array( 'source' => 'wc-updater' ) );
532 }
533 }
534
535 /**
536 * Run manual database update.
537 *
538 * @return void
539 */
540 public static function run_manual_database_update() {
541 self::update();
542 }
543
544 /**
545 * Run an update callback when triggered by ActionScheduler.
546 *
547 * @param string $update_callback Callback name.
548 *
549 * @since 3.6.0
550 *
551 * @return void
552 */
553 public static function run_update_callback( $update_callback ) {
554 include_once __DIR__ . '/wc-update-functions.php';
555
556 if ( is_callable( $update_callback ) ) {
557 self::run_update_callback_start( $update_callback );
558 $result = (bool) call_user_func( $update_callback );
559 self::run_update_callback_end( $update_callback, $result );
560 }
561 }
562
563 /**
564 * Triggered when a callback will run.
565 *
566 * @since 3.6.0
567 * @param string $callback Callback name.
568 *
569 * @return void
570 */
571 protected static function run_update_callback_start( $callback ) {
572 wc_maybe_define_constant( 'WC_UPDATING', true );
573 }
574
575 /**
576 * Triggered when a callback has ran.
577 *
578 * @since 3.6.0
579 * @param string $callback Callback name.
580 * @param bool $result Return value from callback. Non-false need to run again.
581 *
582 * @return void
583 */
584 protected static function run_update_callback_end( $callback, $result ) {
585 if ( $result ) {
586 WC()->queue()->add(
587 'woocommerce_run_update_callback',
588 array(
589 'update_callback' => $callback,
590 ),
591 'woocommerce-db-updates'
592 );
593 }
594 }
595
596 /**
597 * Install actions when a update button is clicked within the admin area.
598 *
599 * This function is hooked into admin_init to affect admin only.
600 *
601 * @return void
602 */
603 public static function install_actions() {
604 if ( ! empty( $_GET['do_update_woocommerce'] ) ) {
605 // WPCS: input var ok.
606 check_admin_referer( 'wc_db_update', 'wc_db_update_nonce' );
607 wc_get_logger()->info( 'Manual database update triggered.', array( 'source' => 'wc-updater' ) );
608 self::update();
609 self::add_update_db_notice();
610
611 $return_url = $_GET['return_url'] ?? ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized below before use.
612 if ( ! empty( $return_url ) ) {
613 // Try to go back to the previous page.
614 if ( 'wc-admin-referer' === $return_url ) {
615 $return_url = preg_replace( '/^' . preg_quote( untrailingslashit( admin_url() ), '/' ) . '\/?/i', '', wp_get_referer() ? wp_get_referer() : '' );
616
617 if ( $return_url && false === strpos( $return_url, 'do_update_woocommerce' ) ) {
618 $return_url = remove_query_arg(
619 array( '_wpnonce', '_wc_notice_nonce', 'wc_db_update', 'wc_db_update_nonce', 'wc-hide-notice' ),
620 admin_url( $return_url )
621 );
622 } else {
623 $return_url = admin_url( 'admin.php?page=wc-settings' );
624 }
625 }
626
627 $return_url = esc_url_raw( wp_unslash( $return_url ) );
628 wp_safe_redirect( $return_url );
629 // WPCS: input var ok.
630 exit;
631 }
632 }
633 }
634
635 /**
636 * Install WC.
637 *
638 * @return void
639 */
640 public static function install() {
641 if ( ! is_blog_installed() ) {
642 return;
643 }
644
645 // Create a lock to prevent multiple installs from running simultaneously.
646 if ( ! self::create_lock() ) {
647 return;
648 }
649
650 try {
651 wc_maybe_define_constant( 'WC_INSTALLING', true );
652 self::install_core();
653 } finally {
654 self::release_lock();
655 }
656
657 // Use add_option() here to avoid overwriting this value with each
658 // plugin version update. We base plugin age off of this value.
659 add_option( 'woocommerce_admin_install_timestamp', time() );
660
661 // Force a flush of rewrite rules even if the corresponding hook isn't initialized yet.
662 if ( ! has_action( 'woocommerce_flush_rewrite_rules' ) ) {
663 flush_rewrite_rules();
664 }
665
666 /**
667 * Flush the rewrite rules after install or update.
668 *
669 * @since 2.7.0
670 */
671 do_action( 'woocommerce_flush_rewrite_rules' );
672 /**
673 * Run after WooCommerce has been installed or updated.
674 *
675 * @since 3.2.0
676 */
677 do_action( 'woocommerce_installed' );
678 /**
679 * Run after WooCommerce Admin has been installed or updated.
680 *
681 * @since 6.5.0
682 */
683 do_action( 'woocommerce_admin_installed' );
684 }
685
686 /**
687 * Core function that performs the WooCommerce install.
688 *
689 * @return void
690 */
691 private static function install_core() {
692 if ( self::is_new_install() && ! get_option( self::NEWLY_INSTALLED_OPTION, false ) ) {
693 update_option( self::NEWLY_INSTALLED_OPTION, 'yes' );
694 }
695
696 WC()->wpdb_table_fix();
697 self::remove_admin_notices();
698 self::create_tables();
699 self::verify_base_tables();
700 self::create_options();
701 self::migrate_options();
702 self::create_roles();
703 self::setup_environment();
704 self::create_terms();
705 self::clear_cron_jobs();
706 self::delete_obsolete_notes();
707 self::create_files();
708 self::maybe_create_pages();
709 self::maybe_set_activation_transients();
710 self::set_paypal_standard_load_eligibility();
711 self::update_wc_version();
712 self::maybe_update_db_version();
713 self::maybe_set_store_id();
714 }
715
716 /**
717 * Attempts to acquire an installation lock.
718 *
719 * @return bool True if a lock was acquired, otherwise false.
720 */
721 private static function create_lock(): bool {
722 global $wpdb;
723
724 // Insert will fail if it already exists so this functions as a mutex.
725 $created_lock = $wpdb->query(
726 $wpdb->prepare(
727 "INSERT INTO {$wpdb->options} (option_name, option_value, autoload) VALUES ('wc_installing', %d, 'no')",
728 time()
729 )
730 );
731
732 // Take over the lock if it's stale (older than 10 minutes).
733 if ( ! $created_lock ) {
734 $created_lock = $wpdb->query(
735 $wpdb->prepare(
736 "UPDATE {$wpdb->options} SET option_value = %d WHERE option_name = 'wc_installing' AND option_value < %d",
737 time(),
738 time() - ( MINUTE_IN_SECONDS * 10 )
739 )
740 );
741 }
742
743 if ( $created_lock ) {
744 // Set the transient for backward compatibility in case others are relying on it to signal an ongoing install.
745 set_transient( 'wc_installing', 'yes', MINUTE_IN_SECONDS * 10 );
746 return true;
747 }
748
749 return false;
750 }
751
752 /**
753 * Releases the installation lock.
754 */
755 private static function release_lock(): void {
756 // Delete the transient BEFORE the option to avoid races that might result in an active lock with an empty transient.
757 delete_transient( 'wc_installing' );
758
759 global $wpdb;
760 $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name = 'wc_installing'" );
761 }
762
763 /**
764 * Get the names of any missing WooCommerce base tables.
765 *
766 * Unlike verify_base_tables(), this method has no side effects: it only inspects
767 * the database and reports which required tables are missing.
768 *
769 * @since 11.0.0
770 *
771 * @return string[] List of missing table names.
772 */
773 public static function get_missing_base_tables(): array {
774 $schema = self::get_schema();
775
776 $hpos_settings = filter_var_array(
777 array(
778 'cot' => get_option( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION ),
779 'data_sync' => get_option( DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION ),
780 ),
781 array(
782 'cot' => FILTER_VALIDATE_BOOLEAN,
783 'data_sync' => FILTER_VALIDATE_BOOLEAN,
784 )
785 );
786 if ( in_array( true, $hpos_settings, true ) ) {
787 $schema .= wc_get_container()
788 ->get( OrdersTableDataStore::class )
789 ->get_database_schema();
790 }
791
792 return wc_get_container()
793 ->get( DatabaseUtil::class )
794 ->get_missing_tables( $schema );
795 }
796
797 /**
798 * Check if all the base tables are present, updating the stored schema status accordingly.
799 *
800 * @param bool $modify_notice Whether to modify notice based on if all tables are present.
801 * @param bool $execute Whether to execute get_schema queries as well.
802 *
803 * @return string[] List of missing table names.
804 */
805 public static function verify_base_tables( $modify_notice = true, $execute = false ) {
806 if ( $execute ) {
807 self::create_tables();
808 }
809
810 $missing_tables = self::get_missing_base_tables();
811
812 if ( 0 < count( $missing_tables ) ) {
813 update_option( 'woocommerce_schema_missing_tables', $missing_tables );
814 } else {
815 if ( $modify_notice ) {
816 WC_Admin_Notices::remove_notice( 'base_tables_missing' );
817 }
818 update_option( 'woocommerce_schema_version', WC()->db_version );
819 delete_option( 'woocommerce_schema_missing_tables' );
820 }
821 return $missing_tables;
822 }
823
824 /**
825 * Reset any notices added to admin.
826 *
827 * @since 3.2.0
828 *
829 * @return void
830 */
831 private static function remove_admin_notices() {
832 include_once __DIR__ . '/admin/class-wc-admin-notices.php';
833
834 WC_Admin_Notices::remove_all_notices();
835 self::remove_update_db_notice();
836 }
837
838 /**
839 * Setup WC environment - post types, taxonomies, endpoints.
840 *
841 * @since 3.2.0
842 *
843 * @return void
844 */
845 private static function setup_environment() {
846 WC_Post_types::register_post_types();
847 WC_Post_types::register_taxonomies();
848 WC()->query->init_query_vars();
849 WC()->query->add_endpoints();
850 WC_Auth::add_endpoint();
851 TransientFilesEngine::add_endpoint();
852 }
853
854 /**
855 * Is this a brand new WC install?
856 *
857 * A brand-new installation has no version yet. Also treat empty installations as 'new'.
858 *
859 * @since 11.0.0 returns false early for stores that are already live or have completed onboarding.
860 * @since 3.2.0
861 *
862 * @return boolean
863 */
864 public static function is_new_install() {
865 // Performance note: woocommerce_version is absent before the very first install routine completes.
866 if ( false === get_option( 'woocommerce_version' ) ) {
867 return true;
868 }
869
870 // Performance note: verify if the store is live. This option is auto-loaded, and verification is essentially free.
871 if ( 'no' === get_option( 'woocommerce_coming_soon', 'yes' ) ) {
872 return false;
873 }
874
875 // Performance note: verify if onboarding is complete. This option is auto-loaded, and verification is essentially free.
876 if ( in_array( 'setup', (array) get_option( 'woocommerce_task_list_completed_lists', array() ), true ) ) {
877 return false;
878 }
879
880 // Performance note: this is the original fallback. The store setup is incomplete, and even with a cold cache, we do not anticipate performance issues.
881 return -1 === wc_get_page_id( 'shop' ) && 0 === array_sum( wc_get_container()->get( ProductUtil::class )->get_counts_for_type( 'product' ) );
882 }
883
884 /**
885 * Is a DB update needed?
886 *
887 * @since 3.2.0
888 * @return boolean
889 */
890 public static function needs_db_update() {
891 $current_db_version = get_option( 'woocommerce_db_version', null );
892
893 return ! is_null( $current_db_version ) && version_compare( $current_db_version, array_key_last( self::$db_updates ), '<' );
894 }
895
896 /**
897 * Is DB auto-update enabled? This controls whether database updates are applied without prompting the admin.
898 * This is the default behavior since 9.9.0 and can be overridden via filter 'woocommerce_enable_auto_update_db'.
899 *
900 * @since 9.9.0
901 *
902 * @return bool TRUE if database auto-updates are enabled. FALSE otherwise.
903 */
904 public static function is_db_auto_update_enabled(): bool {
905 /**
906 * Allow WooCommerce to auto-update without prompting the user.
907 *
908 * @since 3.2.0
909 */
910 return (bool) apply_filters( 'woocommerce_enable_auto_update_db', true );
911 }
912
913 /**
914 * See if we need to set redirect transients for activation or not.
915 *
916 * @since 4.6.0
917 *
918 * @return void
919 */
920 private static function maybe_set_activation_transients() {
921 if ( self::is_new_install() ) {
922 set_transient( '_wc_activation_redirect', 1, 30 );
923 }
924 }
925
926 /**
927 * See if we need to show or run database updates during install.
928 *
929 * @since 3.2.0
930 *
931 * @return void
932 */
933 private static function maybe_update_db_version() {
934 if ( self::needs_db_update() ) {
935 /**
936 * Allow WooCommerce to auto-update without prompting the user.
937 *
938 * @since 3.2.0
939 */
940 if ( self::is_db_auto_update_enabled() ) {
941 wc_get_logger()->info( 'Automatic database update triggered.', array( 'source' => 'wc-updater' ) );
942 self::update();
943 } else {
944 self::add_update_db_notice();
945 }
946 } else {
947 self::update_db_version();
948 }
949 }
950
951 /**
952 * Set the Store ID if not already present.
953 *
954 * @since 8.4.0
955 *
956 * @return void
957 */
958 public static function maybe_set_store_id() {
959 if ( ! get_option( self::STORE_ID_OPTION, false ) ) {
960 add_option( self::STORE_ID_OPTION, wp_generate_uuid4() );
961 }
962 }
963
964 /**
965 * Update WC version to current.
966 *
967 * @return void
968 */
969 private static function update_wc_version() {
970 update_option( 'woocommerce_version', WC()->version );
971 }
972
973 /**
974 * Get list of DB update callbacks.
975 *
976 * @since 3.0.0
977 * @return array
978 */
979 public static function get_db_update_callbacks() {
980 return self::$db_updates;
981 }
982
983 /**
984 * Push all needed DB updates to the queue for processing.
985 *
986 * @return void
987 */
988 private static function update() {
989 $current_db_version = get_option( 'woocommerce_db_version' );
990 $updates = self::get_db_update_callbacks();
991 $scheduled_time = time();
992
993 wc_get_logger()->info(
994 sprintf( 'Scheduling database updates (from %s)...', $current_db_version ),
995 array( 'source' => 'wc-updater' )
996 );
997
998 if ( self::is_db_auto_update_enabled() ) {
999 /**
1000 * Filters the delay in seconds to apply to the scheduling of database updates when automatic updates are
1001 * enabled.
1002 *
1003 * @since 9.9.0
1004 *
1005 * @param int $delay Delay to add (in seconds). Default is 0 (updates will run as soon as possible).
1006 */
1007 $scheduled_time_delay = absint( apply_filters( 'woocommerce_db_update_schedule_delay', 0 ) );
1008
1009 if ( $scheduled_time_delay > 0 ) {
1010 wc_get_logger()->info(
1011 sprintf( ' Updates will begin running in approximately %s.', human_time_diff( 0, $scheduled_time_delay ) ),
1012 array( 'source' => 'wc-updater' )
1013 );
1014 $scheduled_time += $scheduled_time_delay;
1015 }
1016 }
1017
1018 $loop = 0;
1019 foreach ( $updates as $version => $update_callbacks ) {
1020 if ( version_compare( $current_db_version, $version, '>=' ) ) {
1021 continue;
1022 }
1023
1024 foreach ( $update_callbacks as $update_callback ) {
1025 WC()->queue()->schedule_single(
1026 $scheduled_time + $loop,
1027 'woocommerce_run_update_callback',
1028 array(
1029 'update_callback' => $update_callback,
1030 ),
1031 'woocommerce-db-updates'
1032 );
1033 ++$loop;
1034
1035 wc_get_logger()->info(
1036 sprintf( ' [%s] Scheduled \'%s\'.', $version, $update_callback ),
1037 array( 'source' => 'wc-updater' )
1038 );
1039 }
1040 }
1041
1042 // After the callbacks finish, update the db version to the current WC db version.
1043 $wc_db_version = array_key_last( $updates );
1044 $wc_db_version = version_compare( WC()->version, $wc_db_version, '>' ) ? WC()->version : $wc_db_version;
1045
1046 $success = true;
1047 if ( version_compare( $current_db_version, $wc_db_version, '<' ) &&
1048 ! WC()->queue()->get_next( 'woocommerce_update_db_to_current_version' ) ) {
1049 $success = WC()->queue()->schedule_single(
1050 $scheduled_time + $loop,
1051 'woocommerce_update_db_to_current_version',
1052 array(
1053 'version' => $wc_db_version,
1054 ),
1055 'woocommerce-db-updates'
1056 ) > 0;
1057 }
1058
1059 if ( ! $success ) {
1060 wc_get_logger()->error( 'There was an error scheduling database updates.', array( 'source' => 'wc-updater' ) );
1061
1062 // Revert back to nudge so updates are not missed.
1063 if ( self::is_db_auto_update_enabled() ) {
1064 self::add_update_db_notice();
1065 }
1066
1067 return;
1068 }
1069
1070 wc_get_logger()->info( 'Database updates scheduled.', array( 'source' => 'wc-updater' ) );
1071 }
1072
1073 /**
1074 * Update DB version to current.
1075 *
1076 * @param string|null $version New WooCommerce DB version or null.
1077 *
1078 * @return void
1079 */
1080 public static function update_db_version( $version = null ) {
1081 if ( is_null( $version ) ) {
1082 $last_db_version = array_key_last( self::$db_updates );
1083 $version = version_compare( WC()->version, $last_db_version, '>' ) ? WC()->version : $last_db_version;
1084 }
1085
1086 update_option( 'woocommerce_db_version', $version );
1087 }
1088
1089 /**
1090 * Add more cron schedules.
1091 *
1092 * @param array $schedules List of WP scheduled cron jobs.
1093 *
1094 * @return array
1095 */
1096 public static function cron_schedules( $schedules ) {
1097 $schedules['monthly'] = array(
1098 'interval' => MONTH_IN_SECONDS,
1099 'display' => __( 'Monthly', 'woocommerce' ),
1100 );
1101 $schedules['fifteendays'] = array(
1102 'interval' => 15 * DAY_IN_SECONDS,
1103 'display' => __( 'Every 15 Days', 'woocommerce' ),
1104 );
1105 return $schedules;
1106 }
1107
1108 /**
1109 * Removes old cron jobs now that we moved to Action Scheduler.
1110 *
1111 * @return void
1112 */
1113 private static function clear_cron_jobs() {
1114 wp_clear_scheduled_hook( 'woocommerce_scheduled_sales' );
1115 wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
1116 wp_clear_scheduled_hook( 'woocommerce_cleanup_sessions' );
1117 wp_clear_scheduled_hook( 'woocommerce_cleanup_personal_data' );
1118 wp_clear_scheduled_hook( 'woocommerce_cleanup_logs' );
1119 wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
1120 wp_clear_scheduled_hook( 'woocommerce_tracker_send_event' );
1121 wp_clear_scheduled_hook( 'woocommerce_cleanup_rate_limits' );
1122 }
1123
1124 /**
1125 * Create pages on installation.
1126 *
1127 * @return void
1128 */
1129 public static function maybe_create_pages() {
1130 if ( empty( get_option( 'woocommerce_db_version' ) ) ) {
1131 self::create_pages();
1132 }
1133 }
1134
1135 /**
1136 * Create pages that the plugin relies on, storing page IDs in variables.
1137 *
1138 * @return void
1139 */
1140 public static function create_pages() {
1141 // WordPress sets fresh_site to 0 after a page gets published.
1142 // Prevent fresh_site option from being set to 0 so that we can use it for further customizations.
1143 remove_action( 'publish_page', '_delete_option_fresh_site', 0 );
1144
1145 // Set the locale to the store locale to ensure pages are created in the correct language.
1146 wc_switch_to_site_locale();
1147
1148 include_once __DIR__ . '/admin/wc-admin-functions.php';
1149
1150 /**
1151 * Determines the cart shortcode tag used for the cart page.
1152 *
1153 * @since 2.1.0
1154 * @deprecated 8.3.0 This filter is deprecated and will be removed in future versions.
1155 */
1156 $cart_shortcode = apply_filters_deprecated( 'woocommerce_cart_shortcode_tag', array( '' ), '8.3.0', 'woocommerce_create_pages' );
1157
1158 $cart_page_content = empty( $cart_shortcode ) ? self::get_cart_block_content() : '<!-- wp:shortcode -->[' . $cart_shortcode . ']<!-- /wp:shortcode -->';
1159
1160 /**
1161 * Determines the checkout shortcode tag used on the checkout page.
1162 *
1163 * @since 2.1.0
1164 * @deprecated 8.3.0 This filter is deprecated and will be removed in future versions.
1165 */
1166 $checkout_shortcode = apply_filters_deprecated( 'woocommerce_checkout_shortcode_tag', array( '' ), '8.3.0', 'woocommerce_create_pages' );
1167
1168 $checkout_page_content = empty( $checkout_shortcode ) ? self::get_checkout_block_content() : '<!-- wp:shortcode -->[' . $checkout_shortcode . ']<!-- /wp:shortcode -->';
1169
1170 /**
1171 * Determines the my account shortcode tag used on the my account page.
1172 *
1173 * @since 2.1.0
1174 */
1175 $my_account_shortcode = apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' );
1176
1177 /**
1178 * Determines which pages are created during install.
1179 *
1180 * @since 2.1.0
1181 */
1182 $pages = apply_filters(
1183 'woocommerce_create_pages',
1184 array(
1185 'shop' => array(
1186 'name' => _x( 'shop', 'Page slug', 'woocommerce' ),
1187 'title' => _x( 'Shop', 'Page title', 'woocommerce' ),
1188 'content' => '',
1189 ),
1190 'cart' => array(
1191 'name' => _x( 'cart', 'Page slug', 'woocommerce' ),
1192 'title' => _x( 'Cart', 'Page title', 'woocommerce' ),
1193 'content' => $cart_page_content,
1194 ),
1195 'checkout' => array(
1196 'name' => _x( 'checkout', 'Page slug', 'woocommerce' ),
1197 'title' => _x( 'Checkout', 'Page title', 'woocommerce' ),
1198 'content' => $checkout_page_content,
1199 ),
1200 'myaccount' => array(
1201 'name' => _x( 'my-account', 'Page slug', 'woocommerce' ),
1202 'title' => _x( 'My account', 'Page title', 'woocommerce' ),
1203 'content' => '<!-- wp:shortcode -->[' . $my_account_shortcode . ']<!-- /wp:shortcode -->',
1204 ),
1205 'refund_returns' => array(
1206 'name' => _x( 'refund_returns', 'Page slug', 'woocommerce' ),
1207 'title' => _x( 'Refund and Returns Policy', 'Page title', 'woocommerce' ),
1208 'content' => self::get_refunds_return_policy_page_content(),
1209 'post_status' => 'draft',
1210 ),
1211 )
1212 );
1213
1214 foreach ( $pages as $key => $page ) {
1215 wc_create_page(
1216 esc_sql( $page['name'] ),
1217 'woocommerce_' . $key . '_page_id',
1218 $page['title'],
1219 $page['content'],
1220 ! empty( $page['parent'] ) ? wc_get_page_id( $page['parent'] ) : '',
1221 ! empty( $page['post_status'] ) ? $page['post_status'] : 'publish'
1222 );
1223 }
1224
1225 // Restore the locale to the default locale.
1226 wc_restore_locale();
1227 }
1228
1229 /**
1230 * Default options.
1231 *
1232 * Sets up the default options used on the settings page.
1233 *
1234 * @return void
1235 */
1236 private static function create_options() {
1237 // Include settings so that we can run through defaults.
1238 include_once __DIR__ . '/admin/class-wc-admin-settings.php';
1239
1240 $settings = WC_Admin_Settings::get_settings_pages();
1241
1242 foreach ( $settings as $section ) {
1243 if ( ! is_a( $section, 'WC_Settings_Page' ) || ! method_exists( $section, 'get_settings' ) ) {
1244 continue;
1245 }
1246 $subsections = array_unique( array_merge( array( '' ), array_keys( $section->get_sections() ) ) );
1247
1248 /**
1249 * We are using 'WC_Settings_Page::get_settings' on purpose even thought it's deprecated.
1250 * See the method documentation for an explanation.
1251 */
1252
1253 foreach ( $subsections as $subsection ) {
1254 foreach ( $section->get_settings( $subsection ) as $value ) {
1255 if ( isset( $value['default'] ) && isset( $value['id'] ) ) {
1256 $autoload = isset( $value['autoload'] ) ? (bool) $value['autoload'] : true;
1257 $skip_initial_save = isset( $value['skip_initial_save'] ) ? (bool) $value['skip_initial_save'] : false;
1258 if ( ! $skip_initial_save ) {
1259 add_option( $value['id'], $value['default'], '', ( $autoload ? 'yes' : 'no' ) );
1260 }
1261 }
1262 }
1263 }
1264 }
1265
1266 // Define other defaults if not in setting screens.
1267 add_option( 'woocommerce_single_image_width', '600', '', 'yes' );
1268 add_option( 'woocommerce_thumbnail_image_width', '300', '', 'yes' );
1269 add_option( 'woocommerce_checkout_highlight_required_fields', 'yes', '', 'yes' );
1270 add_option( 'woocommerce_demo_store', 'no', '', 'no' );
1271
1272 if ( self::is_new_install() ) {
1273 // Define initial tax classes.
1274 WC_Tax::create_tax_class( __( 'Reduced rate', 'woocommerce' ) );
1275 WC_Tax::create_tax_class( __( 'Zero rate', 'woocommerce' ) );
1276
1277 // For new installs, setup and enable Approved Product Download Directories.
1278 wc_get_container()->get( Download_Directories_Sync::class )->init_feature( false, true );
1279 }
1280 }
1281
1282 /**
1283 * Enable HPOS by default for new shops.
1284 *
1285 * @since 8.2.0
1286 *
1287 * @return void
1288 */
1289 public static function maybe_enable_hpos() {
1290 if ( self::should_enable_hpos_for_new_shop() ) {
1291 $feature_controller = wc_get_container()->get( FeaturesController::class );
1292 $feature_controller->change_feature_enable( 'custom_order_tables', true );
1293 }
1294 }
1295
1296 /**
1297 * Add the coming soon options for new shops.
1298 *
1299 * Ensure that the options are set for all shops for performance even if core profiler is disabled on the host.
1300 *
1301 * @since 9.3.0
1302 *
1303 * @return void
1304 */
1305 public static function add_coming_soon_option() {
1306 add_option( 'woocommerce_coming_soon', 'yes' );
1307 add_option( 'woocommerce_store_pages_only', 'yes' );
1308 }
1309
1310 /**
1311 * Enable email improvements by default for new shops.
1312 *
1313 * @since 9.8.0
1314 *
1315 * @return void
1316 */
1317 public static function enable_email_improvements_for_newly_installed() {
1318 $feature_controller = wc_get_container()->get( FeaturesController::class );
1319 $feature_controller->change_feature_enable( 'email_improvements', true );
1320 update_option( 'woocommerce_email_improvements_default_enabled', 'yes' );
1321 update_option( 'woocommerce_email_auto_sync_with_theme', 'yes' );
1322 update_option( 'woocommerce_email_improvements_first_enabled_at', gmdate( 'Y-m-d H:i:s' ) );
1323 update_option( 'woocommerce_email_improvements_last_enabled_at', gmdate( 'Y-m-d H:i:s' ) );
1324 update_option( 'woocommerce_email_improvements_enabled_count', 1 );
1325 }
1326
1327 /**
1328 * Enable customer stock notifications signups by default for new shops.
1329 *
1330 * @since 0.0.0
1331 *
1332 * @return void
1333 */
1334 public static function enable_customer_stock_notifications_signups() {
1335 update_option( 'woocommerce_back_in_stock_allow_signups', 'yes' );
1336 }
1337
1338 /**
1339 * Set scheduled import mode as the default for new installations.
1340 *
1341 * Uses add_option() which only sets the option if it doesn't already exist.
1342 * This ensures existing installations are not affected.
1343 *
1344 * @since 10.5.0
1345 *
1346 * @return void
1347 */
1348 public static function enable_analytics_scheduled_import(): void {
1349 // add_option only sets if option doesn't exist, returns false if it already exists.
1350 add_option( 'woocommerce_analytics_scheduled_import', 'yes' );
1351 }
1352
1353 /**
1354 * Enable product object caching by default for new shops.
1355 *
1356 * Only newly installed stores get the feature enabled here; existing installs keep the
1357 * opt-in default so their behavior is unchanged on upgrade.
1358 *
1359 * @since 11.0.0
1360 *
1361 * @return void
1362 */
1363 public static function enable_product_instance_caching_for_newly_installed(): void {
1364 $feature_controller = wc_get_container()->get( FeaturesController::class );
1365 $feature_controller->change_feature_enable( ProductCacheController::FEATURE_NAME, true );
1366 }
1367
1368 /**
1369 * Enable email improvements by default for existing shops if conditions are met.
1370 *
1371 * @since 9.9.0
1372 *
1373 * @return void
1374 */
1375 public static function enable_email_improvements_for_existing_merchants() {
1376 if ( ! EmailImprovements::should_enable_email_improvements_for_existing_stores() ) {
1377 return;
1378 }
1379 $feature_controller = wc_get_container()->get( FeaturesController::class );
1380 $feature_controller->change_feature_enable( 'email_improvements', true );
1381 update_option( 'woocommerce_email_improvements_existing_store_enabled', 'yes' );
1382 $first_enabled_at = get_option( 'woocommerce_email_improvements_first_enabled_at' );
1383 if ( ! $first_enabled_at ) {
1384 update_option( 'woocommerce_email_improvements_first_enabled_at', gmdate( 'Y-m-d H:i:s' ) );
1385 }
1386 $last_enabled_at = get_option( 'woocommerce_email_improvements_last_enabled_at' );
1387 if ( ! $last_enabled_at ) {
1388 update_option( 'woocommerce_email_improvements_last_enabled_at', gmdate( 'Y-m-d H:i:s' ) );
1389 }
1390 $enabled_count = get_option( 'woocommerce_email_improvements_enabled_count' );
1391 if ( ! $enabled_count ) {
1392 update_option( 'woocommerce_email_improvements_enabled_count', 1 );
1393 } else {
1394 update_option( 'woocommerce_email_improvements_enabled_count', (int) $enabled_count + 1 );
1395 }
1396 }
1397
1398 /**
1399 * Checks whether HPOS should be enabled for new shops.
1400 *
1401 * @return bool
1402 */
1403 private static function should_enable_hpos_for_new_shop() {
1404 if ( ! did_action( 'woocommerce_init' ) && ! doing_action( 'woocommerce_init' ) ) {
1405 return false;
1406 }
1407
1408 $feature_controller = wc_get_container()->get( FeaturesController::class );
1409
1410 if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
1411 return true;
1412 }
1413
1414 if ( ! empty( wc_get_orders( array( 'limit' => 1 ) ) ) ) {
1415 return false;
1416 }
1417
1418 $plugin_compat_info = $feature_controller->get_compatible_plugins_for_feature( 'custom_order_tables', true );
1419 if ( ! empty( $plugin_compat_info['incompatible'] ) || ! empty( $plugin_compat_info['uncertain'] ) ) {
1420 return false;
1421 }
1422
1423 /**
1424 * Filter to enable HPOS by default for new shops.
1425 *
1426 * @since 8.2.0
1427 */
1428 return apply_filters( 'woocommerce_enable_hpos_by_default_for_new_shops', true );
1429 }
1430
1431 /**
1432 * Get the wc_order_stats table schema.
1433 *
1434 * @since 10.4.0
1435 * @param string $collate Database collation.
1436 * @return string SQL schema for wc_order_stats table.
1437 */
1438 private static function get_order_stats_table_schema( $collate ) {
1439 global $wpdb;
1440
1441 $should_have_fulfillment_column = self::is_new_install() && FeaturesUtil::feature_is_enabled( 'fulfillments' );
1442 if ( false === $should_have_fulfillment_column ) {
1443 $should_have_fulfillment_column = OrdersStatsDataStore::has_fulfillment_status_column();
1444 }
1445
1446 return "CREATE TABLE {$wpdb->prefix}wc_order_stats (
1447 order_id bigint(20) unsigned NOT NULL,
1448 parent_id bigint(20) unsigned DEFAULT 0 NOT NULL,
1449 date_created datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
1450 date_created_gmt datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
1451 date_paid datetime DEFAULT '0000-00-00 00:00:00',
1452 date_completed datetime DEFAULT '0000-00-00 00:00:00',
1453 num_items_sold int(11) DEFAULT 0 NOT NULL,
1454 total_sales double DEFAULT 0 NOT NULL,
1455 tax_total double DEFAULT 0 NOT NULL,
1456 shipping_total double DEFAULT 0 NOT NULL,
1457 net_total double DEFAULT 0 NOT NULL,
1458 returning_customer tinyint(1) DEFAULT NULL,
1459 status varchar(20) NOT NULL,
1460 customer_id bigint(20) unsigned NOT NULL" .
1461 ( $should_have_fulfillment_column ? ',
1462 fulfillment_status varchar(50) DEFAULT NULL' : '' ) . ',
1463 PRIMARY KEY (order_id),
1464 KEY date_created (date_created),
1465 KEY customer_id (customer_id),
1466 KEY status (status)' .
1467 ( $should_have_fulfillment_column ? ',
1468 KEY fulfillment_status (fulfillment_status)' : '' ) . ",
1469 KEY idx_date_paid_status_parent (date_paid, status, parent_id)
1470 ) $collate;";
1471 }
1472
1473 /**
1474 * Delete obsolete notes.
1475 *
1476 * @return void
1477 */
1478 public static function delete_obsolete_notes() {
1479 global $wpdb;
1480 $obsolete_notes_names = array(
1481 'wc-admin-welcome-note',
1482 'wc-admin-insight-first-product-and-payment',
1483 'wc-admin-store-notice-setting-moved',
1484 'wc-admin-store-notice-giving-feedback',
1485 'wc-admin-first-downloadable-product',
1486 'wc-admin-learn-more-about-product-settings',
1487 'wc-admin-adding-and-managing-products',
1488 'wc-admin-onboarding-profiler-reminder',
1489 'wc-admin-historical-data',
1490 'wc-admin-manage-store-activity-from-home-screen',
1491 'wc-admin-review-shipping-settings',
1492 'wc-admin-home-screen-feedback',
1493 'wc-admin-update-store-details',
1494 'wc-admin-effortless-payments-by-mollie',
1495 'wc-admin-google-ads-and-marketing',
1496 'wc-admin-insight-first-sale',
1497 'wc-admin-marketing-intro',
1498 'wc-admin-draw-attention',
1499 'wc-admin-welcome-to-woocommerce-for-store-users',
1500 'wc-admin-need-some-inspiration',
1501 'wc-admin-choose-niche',
1502 'wc-admin-start-dropshipping-business',
1503 'wc-admin-filter-by-product-variations-in-reports',
1504 'wc-admin-learn-more-about-variable-products',
1505 'wc-admin-getting-started-ecommerce-webinar',
1506 'wc-admin-navigation-feedback',
1507 'wc-admin-navigation-feedback-follow-up',
1508 'wc-admin-set-up-additional-payment-types',
1509 'wc-admin-deactivate-plugin',
1510 'wc-admin-complete-store-details',
1511 'wc-admin-choosing-a-theme',
1512 );
1513
1514 /**
1515 * An array of deprecated notes to delete on update.
1516 *
1517 * @since 6.5.0
1518 */
1519 $additional_obsolete_notes_names = apply_filters(
1520 'woocommerce_admin_obsolete_notes_names',
1521 array()
1522 );
1523
1524 if ( is_array( $additional_obsolete_notes_names ) ) {
1525 $obsolete_notes_names = array_merge(
1526 $obsolete_notes_names,
1527 $additional_obsolete_notes_names
1528 );
1529 }
1530
1531 $note_names_placeholder = substr( str_repeat( ',%s', count( $obsolete_notes_names ) ), 1 );
1532
1533 $note_ids = $wpdb->get_results(
1534 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Ignored for allowing interpolation in the IN statement.
1535 $wpdb->prepare(
1536 "SELECT note_id FROM {$wpdb->prefix}wc_admin_notes WHERE name IN ( $note_names_placeholder )",
1537 $obsolete_notes_names
1538 ),
1539 ARRAY_N
1540 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare.
1541 );
1542
1543 if ( ! $note_ids ) {
1544 return;
1545 }
1546
1547 $note_ids = array_column( $note_ids, 0 );
1548 $note_ids_placeholder = substr( str_repeat( ',%d', count( $note_ids ) ), 1 );
1549
1550 $wpdb->query(
1551 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Ignored for allowing interpolation in the IN statement.
1552 $wpdb->prepare(
1553 "DELETE FROM {$wpdb->prefix}wc_admin_notes WHERE note_id IN ( $note_ids_placeholder )",
1554 $note_ids
1555 )
1556 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare.
1557 );
1558
1559 $wpdb->query(
1560 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- Ignored for allowing interpolation in the IN statement.
1561 $wpdb->prepare(
1562 "DELETE FROM {$wpdb->prefix}wc_admin_note_actions WHERE note_id IN ( $note_ids_placeholder )",
1563 $note_ids
1564 )
1565 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare.
1566 );
1567 }
1568
1569 /**
1570 * Migrate option values to their new keys/names.
1571 *
1572 * @return void
1573 */
1574 public static function migrate_options() {
1575
1576 $migrated_options = array(
1577 'woocommerce_onboarding_profile' => 'wc_onboarding_profile',
1578 'woocommerce_admin_install_timestamp' => 'wc_admin_install_timestamp',
1579 'woocommerce_onboarding_opt_in' => 'wc_onboarding_opt_in',
1580 'woocommerce_admin_import_stats' => 'wc_admin_import_stats',
1581 'woocommerce_admin_version' => 'wc_admin_version',
1582 'woocommerce_admin_last_orders_milestone' => 'wc_admin_last_orders_milestone',
1583 'woocommerce_admin-wc-helper-last-refresh' => 'wc-admin-wc-helper-last-refresh',
1584 'woocommerce_admin_report_export_status' => 'wc_admin_report_export_status',
1585 'woocommerce_task_list_complete' => 'woocommerce_task_list_complete',
1586 'woocommerce_task_list_hidden' => 'woocommerce_task_list_hidden',
1587 'woocommerce_extended_task_list_complete' => 'woocommerce_extended_task_list_complete',
1588 'woocommerce_extended_task_list_hidden' => 'woocommerce_extended_task_list_hidden',
1589 );
1590
1591 wc_maybe_define_constant( 'WC_ADMIN_MIGRATING_OPTIONS', true );
1592
1593 foreach ( $migrated_options as $new_option => $old_option ) {
1594 $old_option_value = get_option( $old_option, false );
1595
1596 // Continue if no option value was previously set.
1597 if ( false === $old_option_value ) {
1598 continue;
1599 }
1600
1601 if ( '1' === $old_option_value ) {
1602 $old_option_value = 'yes';
1603 } elseif ( '0' === $old_option_value ) {
1604 $old_option_value = 'no';
1605 }
1606
1607 update_option( $new_option, $old_option_value );
1608 if ( $new_option !== $old_option ) {
1609 delete_option( $old_option );
1610 }
1611 }
1612 }
1613 /**
1614 * Add the default terms for WC taxonomies - product types and order statuses. Modify this at your own risk.
1615 *
1616 * @return void
1617 */
1618 public static function create_terms() {
1619 $taxonomies = array(
1620 'product_type' => array(
1621 ProductType::SIMPLE,
1622 ProductType::GROUPED,
1623 ProductType::VARIABLE,
1624 ProductType::EXTERNAL,
1625 ),
1626 'product_visibility' => array(
1627 'exclude-from-search',
1628 'exclude-from-catalog',
1629 'featured',
1630 'outofstock',
1631 'rated-1',
1632 'rated-2',
1633 'rated-3',
1634 'rated-4',
1635 'rated-5',
1636 ),
1637 );
1638
1639 foreach ( $taxonomies as $taxonomy => $terms ) {
1640 foreach ( $terms as $term ) {
1641 if ( ! get_term_by( 'name', $term, $taxonomy ) ) { // @codingStandardsIgnoreLine.
1642 wp_insert_term( $term, $taxonomy );
1643 }
1644 }
1645 }
1646
1647 $woocommerce_default_category = (int) get_option( 'default_product_cat', 0 );
1648
1649 if ( ! $woocommerce_default_category || ! term_exists( $woocommerce_default_category, 'product_cat' ) ) {
1650 $default_product_cat_id = 0;
1651 $default_product_cat_slug = sanitize_title( _x( 'Uncategorized', 'Default category slug', 'woocommerce' ) );
1652 $default_product_cat = get_term_by( 'slug', $default_product_cat_slug, 'product_cat' ); // @codingStandardsIgnoreLine.
1653
1654 if ( $default_product_cat ) {
1655 $default_product_cat_id = absint( $default_product_cat->term_taxonomy_id );
1656 } else {
1657 $result = wp_insert_term( _x( 'Uncategorized', 'Default category slug', 'woocommerce' ), 'product_cat', array( 'slug' => $default_product_cat_slug ) );
1658
1659 if ( ! is_wp_error( $result ) && ! empty( $result['term_taxonomy_id'] ) ) {
1660 $default_product_cat_id = absint( $result['term_taxonomy_id'] );
1661 }
1662 }
1663
1664 if ( $default_product_cat_id ) {
1665 update_option( 'default_product_cat', $default_product_cat_id );
1666 }
1667 }
1668 }
1669
1670 /**
1671 * Set up the database tables which the plugin needs to function.
1672 * WARNING: If you are modifying this method, make sure that its safe to call regardless of the state of database.
1673 *
1674 * This is called from `install` method and is executed in-sync when WC is installed or updated. This can also be called optionally from `verify_base_tables`.
1675 *
1676 * TODO: Add all crucial tables that we have created from workers in the past.
1677 *
1678 * Tables:
1679 * woocommerce_attribute_taxonomies - Table for storing attribute taxonomies - these are user defined
1680 * woocommerce_downloadable_product_permissions - Table for storing user and guest download permissions.
1681 * KEY(order_id, product_id, download_id) used for organizing downloads on the My Account page
1682 * woocommerce_order_items - Order line items are stored in a table to make them easily queryable for reports
1683 * woocommerce_order_itemmeta - Order line item meta is stored in a table for storing extra data.
1684 * woocommerce_tax_rates - Tax Rates are stored inside 2 tables making tax queries simple and efficient.
1685 * woocommerce_tax_rate_locations - Each rate can be applied to more than one postcode/city hence the second table.
1686 *
1687 * @return array Strings containing the results of the various update queries as returned by dbDelta.
1688 */
1689 public static function create_tables() {
1690 global $wpdb;
1691
1692 $wpdb->hide_errors();
1693
1694 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
1695
1696 /**
1697 * Before updating with DBDELTA, remove any primary keys which could be
1698 * modified due to schema updates.
1699 */
1700 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_downloadable_product_permissions';" ) ) {
1701 if ( ! $wpdb->get_var( "SHOW COLUMNS FROM `{$wpdb->prefix}woocommerce_downloadable_product_permissions` LIKE 'permission_id';" ) ) {
1702 $wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions DROP PRIMARY KEY, ADD `permission_id` bigint(20) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT;" );
1703 }
1704 }
1705
1706 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}wc_order_product_lookup';" ) ) {
1707 if ( 2 > $wpdb->get_var( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '{$wpdb->prefix}wc_order_product_lookup' AND INDEX_NAME = 'PRIMARY'" ) ) {
1708 $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_order_product_lookup DROP PRIMARY KEY, ADD PRIMARY KEY (order_item_id, order_id)" );
1709 }
1710 }
1711
1712 /**
1713 * Change wp_woocommerce_sessions schema to use a bigint auto increment field instead of char(32) field as
1714 * the primary key as it is not a good practice to use a char(32) field as the primary key of a table and as
1715 * there were reports of issues with this table (see https://github.com/woocommerce/woocommerce/issues/20912).
1716 *
1717 * This query needs to run before dbDelta() as this WP function is not able to handle primary key changes
1718 * (see https://github.com/woocommerce/woocommerce/issues/21534 and https://core.trac.wordpress.org/ticket/40357).
1719 */
1720 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_sessions'" ) ) {
1721 if ( ! $wpdb->get_var( "SHOW KEYS FROM {$wpdb->prefix}woocommerce_sessions WHERE Key_name = 'PRIMARY' AND Column_name = 'session_id'" ) ) {
1722 $wpdb->query(
1723 "ALTER TABLE `{$wpdb->prefix}woocommerce_sessions` DROP PRIMARY KEY, DROP KEY `session_id`, ADD PRIMARY KEY(`session_id`), ADD UNIQUE KEY(`session_key`)"
1724 );
1725 }
1726 }
1727
1728 $suppress_errors = $wpdb->suppress_errors( true );
1729 $db_delta_result = dbDelta( self::get_schema() );
1730 $wpdb->suppress_errors( $suppress_errors );
1731
1732 $comment_type_index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE key_name = 'woo_idx_comment_type'" );
1733 if ( null === $comment_type_index_exists ) {
1734 // Add an index to the field comment_type to improve the response time of the query
1735 // used by WC_Comments::wp_count_comments() to get the number of comments by type.
1736 $wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_type (comment_type)" );
1737 }
1738
1739 $date_type_index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE key_name = 'woo_idx_comment_date_type'" );
1740 if ( null === $date_type_index_exists ) {
1741 // Improve performance of the admin comments query when fetching the latest 25 comments while excluding reviews and internal notes.
1742 $wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_date_type (comment_date_gmt, comment_type, comment_approved, comment_post_ID)" );
1743 }
1744
1745 $comment_approved_type_index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE key_name = 'woo_idx_comment_approved_type'" );
1746 if ( null === $comment_approved_type_index_exists ) {
1747 // Improve performance of the admin comments query when counting approved comments while excluding internal notes.
1748 $wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_approved_type (comment_approved, comment_type, comment_post_ID)" );
1749 }
1750
1751 // Clear table caches.
1752 delete_transient( 'wc_attribute_taxonomies' );
1753
1754 return $db_delta_result;
1755 }
1756
1757 /**
1758 * Get Table schema.
1759 *
1760 * See https://github.com/woocommerce/woocommerce/wiki/Database-Description/
1761 *
1762 * A note on indexes; Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
1763 * As of WordPress 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which
1764 * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
1765 *
1766 * Changing indexes may cause duplicate index notices in logs due to https://core.trac.wordpress.org/ticket/34870 but dropping
1767 * indexes first causes too much load on some servers/larger DB.
1768 *
1769 * When adding or removing a table, make sure to update the list of tables in WC_Install::get_tables().
1770 *
1771 * @return string
1772 */
1773 private static function get_schema() {
1774 global $wpdb;
1775
1776 $collate = '';
1777
1778 if ( $wpdb->has_cap( 'collation' ) ) {
1779 $collate = $wpdb->get_charset_collate();
1780 }
1781
1782 $max_index_length = wc_get_container()->get( DatabaseUtil::class )->get_max_index_length();
1783
1784 $product_attributes_lookup_table_creation_sql = wc_get_container()->get( DataRegenerator::class )->get_table_creation_sql();
1785
1786 $feature_controller = wc_get_container()->get( FeaturesController::class );
1787 $hpos_enabled =
1788 $feature_controller->feature_is_enabled( DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION ) || $feature_controller->feature_is_enabled( CustomOrdersTableController::CUSTOM_ORDERS_TABLE_USAGE_ENABLED_OPTION ) ||
1789 self::should_enable_hpos_for_new_shop();
1790 $hpos_table_schema = $hpos_enabled ? wc_get_container()->get( OrdersTableDataStore::class )->get_database_schema() : '';
1791
1792 // Stock Notifications Table Schema.
1793 $stock_notifications_table_schema = wc_get_container()->get( StockNotificationsDataStore::class )->get_database_schema();
1794
1795 // Email Unsubscribes table — generic across email types; each row pairs an email hash with an email-kind identifier.
1796 $email_unsubscribes_table_schema = wc_get_container()->get( \Automattic\WooCommerce\Internal\Email\Unsubscribes\Storage::class )->get_database_schema();
1797 $order_stats_table_schema = self::get_order_stats_table_schema( $collate );
1798
1799 $mysql_version = wc_get_server_database_version()['number'];
1800 if ( version_compare( $mysql_version, '5.6', '>=' ) ) {
1801 $datetime_default = 'DEFAULT CURRENT_TIMESTAMP';
1802 } else {
1803 $datetime_default = "DEFAULT '1970-01-01 00:00:00'";
1804 }
1805
1806 $tables = "
1807 CREATE TABLE {$wpdb->prefix}woocommerce_sessions (
1808 session_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
1809 session_key char(32) NOT NULL,
1810 session_value longtext NOT NULL,
1811 session_expiry bigint(20) unsigned NOT NULL,
1812 PRIMARY KEY (session_id),
1813 KEY session_expiry (session_expiry),
1814 UNIQUE KEY session_key (session_key)
1815 ) $collate;
1816 CREATE TABLE {$wpdb->prefix}woocommerce_api_keys (
1817 key_id bigint(20) unsigned NOT NULL auto_increment,
1818 user_id bigint(20) unsigned NOT NULL,
1819 description varchar(200) NULL,
1820 permissions varchar(10) NOT NULL,
1821 consumer_key char(64) NOT NULL,
1822 consumer_secret char(43) NOT NULL,
1823 nonces longtext NULL,
1824 truncated_key char(7) NOT NULL,
1825 last_access datetime NULL default null,
1826 PRIMARY KEY (key_id),
1827 KEY consumer_key (consumer_key),
1828 KEY consumer_secret (consumer_secret)
1829 ) $collate;
1830 CREATE TABLE {$wpdb->prefix}woocommerce_attribute_taxonomies (
1831 attribute_id bigint(20) unsigned NOT NULL auto_increment,
1832 attribute_name varchar(200) NOT NULL,
1833 attribute_label varchar(200) NULL,
1834 attribute_type varchar(20) NOT NULL,
1835 attribute_orderby varchar(20) NOT NULL,
1836 attribute_public int(1) NOT NULL DEFAULT 1,
1837 PRIMARY KEY (attribute_id),
1838 KEY attribute_name (attribute_name(20))
1839 ) $collate;
1840 CREATE TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions (
1841 permission_id bigint(20) unsigned NOT NULL auto_increment,
1842 download_id varchar(36) NOT NULL,
1843 product_id bigint(20) unsigned NOT NULL,
1844 order_id bigint(20) unsigned NOT NULL DEFAULT 0,
1845 order_key varchar(200) NOT NULL,
1846 user_email varchar(200) NOT NULL,
1847 user_id bigint(20) unsigned NULL,
1848 downloads_remaining varchar(9) NULL,
1849 access_granted datetime NOT NULL default '0000-00-00 00:00:00',
1850 access_expires datetime NULL default null,
1851 download_count bigint(20) unsigned NOT NULL DEFAULT 0,
1852 PRIMARY KEY (permission_id),
1853 KEY download_order_key_product (product_id,order_id,order_key(16),download_id),
1854 KEY download_order_product (download_id,order_id,product_id),
1855 KEY order_id (order_id),
1856 KEY user_order_remaining_expires (user_id,order_id,downloads_remaining,access_expires),
1857 KEY idx_user_email (user_email(100))
1858 ) $collate;
1859 CREATE TABLE {$wpdb->prefix}woocommerce_order_items (
1860 order_item_id bigint(20) unsigned NOT NULL auto_increment,
1861 order_item_name text NOT NULL,
1862 order_item_type varchar(200) NOT NULL DEFAULT '',
1863 order_id bigint(20) unsigned NOT NULL,
1864 PRIMARY KEY (order_item_id),
1865 KEY order_id (order_id)
1866 ) $collate;
1867 CREATE TABLE {$wpdb->prefix}woocommerce_order_itemmeta (
1868 meta_id bigint(20) unsigned NOT NULL auto_increment,
1869 order_item_id bigint(20) unsigned NOT NULL,
1870 meta_key varchar(255) default NULL,
1871 meta_value longtext NULL,
1872 PRIMARY KEY (meta_id),
1873 KEY order_item_id (order_item_id),
1874 KEY meta_key (meta_key(32))
1875 ) $collate;
1876 CREATE TABLE {$wpdb->prefix}woocommerce_tax_rates (
1877 tax_rate_id bigint(20) unsigned NOT NULL auto_increment,
1878 tax_rate_country varchar(2) NOT NULL DEFAULT '',
1879 tax_rate_state varchar(200) NOT NULL DEFAULT '',
1880 tax_rate varchar(8) NOT NULL DEFAULT '',
1881 tax_rate_name varchar(200) NOT NULL DEFAULT '',
1882 tax_rate_priority bigint(20) unsigned NOT NULL,
1883 tax_rate_compound int(1) NOT NULL DEFAULT 0,
1884 tax_rate_shipping int(1) NOT NULL DEFAULT 1,
1885 tax_rate_order bigint(20) unsigned NOT NULL,
1886 tax_rate_class varchar(200) NOT NULL DEFAULT '',
1887 PRIMARY KEY (tax_rate_id),
1888 KEY tax_rate_country (tax_rate_country),
1889 KEY tax_rate_state (tax_rate_state(2)),
1890 KEY tax_rate_class (tax_rate_class(10)),
1891 KEY tax_rate_priority (tax_rate_priority)
1892 ) $collate;
1893 CREATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations (
1894 location_id bigint(20) unsigned NOT NULL auto_increment,
1895 location_code varchar(200) NOT NULL,
1896 tax_rate_id bigint(20) unsigned NOT NULL,
1897 location_type varchar(40) NOT NULL,
1898 PRIMARY KEY (location_id),
1899 KEY tax_rate_id (tax_rate_id),
1900 KEY location_type_code (location_type(10),location_code(20))
1901 ) $collate;
1902 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zones (
1903 zone_id bigint(20) unsigned NOT NULL auto_increment,
1904 zone_name varchar(200) NOT NULL,
1905 zone_order bigint(20) unsigned NOT NULL,
1906 PRIMARY KEY (zone_id),
1907 KEY zone_order_id (zone_order, zone_id)
1908 ) $collate;
1909 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_locations (
1910 location_id bigint(20) unsigned NOT NULL auto_increment,
1911 zone_id bigint(20) unsigned NOT NULL,
1912 location_code varchar(200) NOT NULL,
1913 location_type varchar(40) NOT NULL,
1914 PRIMARY KEY (location_id),
1915 KEY zone_id (zone_id),
1916 KEY location_type_code (location_type(10),location_code(20))
1917 ) $collate;
1918 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_methods (
1919 zone_id bigint(20) unsigned NOT NULL,
1920 instance_id bigint(20) unsigned NOT NULL auto_increment,
1921 method_id varchar(200) NOT NULL,
1922 method_order bigint(20) unsigned NOT NULL,
1923 is_enabled tinyint(1) NOT NULL DEFAULT '1',
1924 PRIMARY KEY (instance_id),
1925 KEY zone_id (zone_id),
1926 KEY method_id (method_id(20))
1927 ) $collate;
1928 CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokens (
1929 token_id bigint(20) unsigned NOT NULL auto_increment,
1930 gateway_id varchar(200) NOT NULL,
1931 token text NOT NULL,
1932 user_id bigint(20) unsigned NOT NULL DEFAULT '0',
1933 type varchar(200) NOT NULL,
1934 is_default tinyint(1) NOT NULL DEFAULT '0',
1935 PRIMARY KEY (token_id),
1936 KEY user_id (user_id)
1937 ) $collate;
1938 CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokenmeta (
1939 meta_id bigint(20) unsigned NOT NULL auto_increment,
1940 payment_token_id bigint(20) unsigned NOT NULL,
1941 meta_key varchar(255) NULL,
1942 meta_value longtext NULL,
1943 PRIMARY KEY (meta_id),
1944 KEY payment_token_id (payment_token_id),
1945 KEY meta_key (meta_key(32))
1946 ) $collate;
1947 CREATE TABLE {$wpdb->prefix}woocommerce_log (
1948 log_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
1949 timestamp datetime NOT NULL,
1950 level smallint(4) NOT NULL,
1951 source varchar(200) NOT NULL,
1952 message longtext NOT NULL,
1953 context longtext NULL,
1954 PRIMARY KEY (log_id),
1955 KEY level (level)
1956 ) $collate;
1957 CREATE TABLE {$wpdb->prefix}wc_webhooks (
1958 webhook_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
1959 status varchar(200) NOT NULL,
1960 name text NOT NULL,
1961 user_id bigint(20) unsigned NOT NULL,
1962 delivery_url text NOT NULL,
1963 secret text NOT NULL,
1964 topic varchar(200) NOT NULL,
1965 date_created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1966 date_created_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1967 date_modified datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1968 date_modified_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1969 api_version smallint(4) NOT NULL,
1970 failure_count smallint(10) NOT NULL DEFAULT '0',
1971 pending_delivery tinyint(1) NOT NULL DEFAULT '0',
1972 PRIMARY KEY (webhook_id),
1973 KEY user_id (user_id)
1974 ) $collate;
1975 CREATE TABLE {$wpdb->prefix}wc_download_log (
1976 download_log_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
1977 timestamp datetime NOT NULL,
1978 permission_id bigint(20) unsigned NOT NULL,
1979 user_id bigint(20) unsigned NULL,
1980 user_ip_address varchar(100) NULL DEFAULT '',
1981 PRIMARY KEY (download_log_id),
1982 KEY permission_id (permission_id),
1983 KEY timestamp (timestamp)
1984 ) $collate;
1985 CREATE TABLE {$wpdb->prefix}wc_product_meta_lookup (
1986 `product_id` bigint(20) NOT NULL,
1987 `sku` varchar(100) NULL default '',
1988 `global_unique_id` varchar(100) NULL default '',
1989 `virtual` tinyint(1) NULL default 0,
1990 `downloadable` tinyint(1) NULL default 0,
1991 `min_price` decimal(19,4) NULL default NULL,
1992 `max_price` decimal(19,4) NULL default NULL,
1993 `onsale` tinyint(1) NULL default 0,
1994 `stock_quantity` double NULL default NULL,
1995 `stock_status` varchar(100) NULL default 'instock',
1996 `rating_count` bigint(20) NULL default 0,
1997 `average_rating` decimal(3,2) NULL default 0.00,
1998 `total_sales` bigint(20) NULL default 0,
1999 `tax_status` varchar(100) NULL default 'taxable',
2000 `tax_class` varchar(100) NULL default '',
2001 PRIMARY KEY (`product_id`),
2002 KEY `virtual` (`virtual`),
2003 KEY `downloadable` (`downloadable`),
2004 KEY `stock_status` (`stock_status`),
2005 KEY `stock_quantity` (`stock_quantity`),
2006 KEY `onsale` (`onsale`),
2007 KEY min_max_price (`min_price`, `max_price`),
2008 KEY sku (sku(50))
2009 ) $collate;
2010 CREATE TABLE {$wpdb->prefix}wc_tax_rate_classes (
2011 tax_rate_class_id bigint(20) unsigned NOT NULL auto_increment,
2012 name varchar(200) NOT NULL DEFAULT '',
2013 slug varchar(200) NOT NULL DEFAULT '',
2014 PRIMARY KEY (tax_rate_class_id),
2015 UNIQUE KEY slug (slug($max_index_length))
2016 ) $collate;
2017 CREATE TABLE {$wpdb->prefix}wc_reserved_stock (
2018 `order_id` bigint(20) NOT NULL,
2019 `product_id` bigint(20) NOT NULL,
2020 `stock_quantity` double NOT NULL DEFAULT 0,
2021 `timestamp` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
2022 `expires` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
2023 PRIMARY KEY (`order_id`, `product_id`),
2024 KEY product_id_expires (product_id, expires)
2025 ) $collate;
2026 CREATE TABLE {$wpdb->prefix}wc_rate_limits (
2027 rate_limit_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
2028 rate_limit_key varchar(200) NOT NULL,
2029 rate_limit_expiry bigint(20) unsigned NOT NULL,
2030 rate_limit_remaining smallint(10) NOT NULL DEFAULT '0',
2031 PRIMARY KEY (rate_limit_id),
2032 UNIQUE KEY rate_limit_key (rate_limit_key($max_index_length))
2033 ) $collate;
2034 $product_attributes_lookup_table_creation_sql
2035 CREATE TABLE {$wpdb->prefix}wc_product_download_directories (
2036 url_id bigint(20) unsigned NOT NULL auto_increment,
2037 url varchar(256) NOT NULL,
2038 enabled tinyint(1) NOT NULL DEFAULT 0,
2039 PRIMARY KEY (url_id),
2040 KEY url (url($max_index_length))
2041 ) $collate;
2042 $order_stats_table_schema
2043 CREATE TABLE {$wpdb->prefix}wc_order_product_lookup (
2044 order_item_id bigint(20) unsigned NOT NULL,
2045 order_id bigint(20) unsigned NOT NULL,
2046 product_id bigint(20) unsigned NOT NULL,
2047 variation_id bigint(20) unsigned NOT NULL,
2048 customer_id bigint(20) unsigned NULL,
2049 date_created datetime $datetime_default NOT NULL,
2050 product_qty int(11) NOT NULL,
2051 product_net_revenue double DEFAULT 0 NOT NULL,
2052 product_gross_revenue double DEFAULT 0 NOT NULL,
2053 coupon_amount double DEFAULT 0 NOT NULL,
2054 tax_amount double DEFAULT 0 NOT NULL,
2055 shipping_amount double DEFAULT 0 NOT NULL,
2056 shipping_tax_amount double DEFAULT 0 NOT NULL,
2057 PRIMARY KEY (order_item_id, order_id),
2058 KEY order_id (order_id),
2059 KEY product_id (product_id),
2060 KEY customer_id (customer_id),
2061 KEY date_created (date_created),
2062 KEY customer_product_date (customer_id, product_id, date_created)
2063 ) $collate;
2064 CREATE TABLE {$wpdb->prefix}wc_order_tax_lookup (
2065 order_id bigint(20) unsigned NOT NULL,
2066 tax_rate_id bigint(20) unsigned NOT NULL,
2067 date_created datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
2068 shipping_tax double DEFAULT 0 NOT NULL,
2069 order_tax double DEFAULT 0 NOT NULL,
2070 total_tax double DEFAULT 0 NOT NULL,
2071 PRIMARY KEY (order_id, tax_rate_id),
2072 KEY tax_rate_id (tax_rate_id),
2073 KEY date_created (date_created)
2074 ) $collate;
2075 CREATE TABLE {$wpdb->prefix}wc_order_coupon_lookup (
2076 order_id bigint(20) unsigned NOT NULL,
2077 coupon_id bigint(20) NOT NULL,
2078 date_created datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
2079 discount_amount double DEFAULT 0 NOT NULL,
2080 PRIMARY KEY (order_id, coupon_id),
2081 KEY coupon_id (coupon_id),
2082 KEY date_created (date_created)
2083 ) $collate;
2084 CREATE TABLE {$wpdb->prefix}wc_admin_notes (
2085 note_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
2086 name varchar(255) NOT NULL,
2087 type varchar(20) NOT NULL,
2088 locale varchar(20) NOT NULL,
2089 title longtext NOT NULL,
2090 content longtext NOT NULL,
2091 content_data longtext NULL default null,
2092 status varchar(200) NOT NULL,
2093 source varchar(200) NOT NULL,
2094 date_created datetime NOT NULL default '0000-00-00 00:00:00',
2095 date_reminder datetime NULL default null,
2096 is_snoozable tinyint(1) DEFAULT 0 NOT NULL,
2097 layout varchar(20) DEFAULT '' NOT NULL,
2098 image varchar(200) NULL DEFAULT NULL,
2099 is_deleted tinyint(1) DEFAULT 0 NOT NULL,
2100 is_read tinyint(1) DEFAULT 0 NOT NULL,
2101 icon varchar(200) NOT NULL default 'info',
2102 PRIMARY KEY (note_id)
2103 ) $collate;
2104 CREATE TABLE {$wpdb->prefix}wc_admin_note_actions (
2105 action_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
2106 note_id bigint(20) unsigned NOT NULL,
2107 name varchar(255) NOT NULL,
2108 label varchar(255) NOT NULL,
2109 query longtext NOT NULL,
2110 status varchar(255) NOT NULL,
2111 actioned_text varchar(255) NOT NULL,
2112 nonce_action varchar(255) NULL DEFAULT NULL,
2113 nonce_name varchar(255) NULL DEFAULT NULL,
2114 PRIMARY KEY (action_id),
2115 KEY note_id (note_id)
2116 ) $collate;
2117 CREATE TABLE {$wpdb->prefix}wc_customer_lookup (
2118 customer_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
2119 user_id bigint(20) unsigned DEFAULT NULL,
2120 username varchar(60) DEFAULT '' NOT NULL,
2121 first_name varchar(255) NOT NULL,
2122 last_name varchar(255) NOT NULL,
2123 email varchar(100) NULL default NULL,
2124 date_last_active timestamp NULL default null,
2125 date_registered timestamp NULL default null,
2126 country char(2) DEFAULT '' NOT NULL,
2127 postcode varchar(20) DEFAULT '' NOT NULL,
2128 city varchar(100) DEFAULT '' NOT NULL,
2129 state varchar(100) DEFAULT '' NOT NULL,
2130 PRIMARY KEY (customer_id),
2131 UNIQUE KEY user_id (user_id),
2132 KEY email (email)
2133 ) $collate;
2134 CREATE TABLE {$wpdb->prefix}wc_category_lookup (
2135 category_tree_id bigint(20) unsigned NOT NULL,
2136 category_id bigint(20) unsigned NOT NULL,
2137 PRIMARY KEY (category_tree_id,category_id)
2138 ) $collate;
2139 $hpos_table_schema;
2140 $stock_notifications_table_schema;
2141 $email_unsubscribes_table_schema;
2142 ";
2143
2144 return $tables;
2145 }
2146
2147 /**
2148 * Return a list of WooCommerce tables. Used to make sure all WC tables are dropped when uninstalling the plugin
2149 * in a single site or multi site environment.
2150 *
2151 * @return array WC tables.
2152 */
2153 public static function get_tables() {
2154 global $wpdb;
2155
2156 $tables = array(
2157 "{$wpdb->prefix}wc_download_log",
2158 "{$wpdb->prefix}wc_product_download_directories",
2159 "{$wpdb->prefix}wc_product_meta_lookup",
2160 "{$wpdb->prefix}wc_tax_rate_classes",
2161 "{$wpdb->prefix}wc_webhooks",
2162 "{$wpdb->prefix}woocommerce_api_keys",
2163 "{$wpdb->prefix}woocommerce_attribute_taxonomies",
2164 "{$wpdb->prefix}woocommerce_downloadable_product_permissions",
2165 "{$wpdb->prefix}woocommerce_log",
2166 "{$wpdb->prefix}woocommerce_order_itemmeta",
2167 "{$wpdb->prefix}woocommerce_order_items",
2168 "{$wpdb->prefix}woocommerce_payment_tokenmeta",
2169 "{$wpdb->prefix}woocommerce_payment_tokens",
2170 "{$wpdb->prefix}woocommerce_sessions",
2171 "{$wpdb->prefix}woocommerce_shipping_zone_locations",
2172 "{$wpdb->prefix}woocommerce_shipping_zone_methods",
2173 "{$wpdb->prefix}woocommerce_shipping_zones",
2174 "{$wpdb->prefix}woocommerce_tax_rate_locations",
2175 "{$wpdb->prefix}woocommerce_tax_rates",
2176 "{$wpdb->prefix}wc_reserved_stock",
2177 "{$wpdb->prefix}wc_rate_limits",
2178 "{$wpdb->prefix}wc_product_attributes_lookup",
2179 "{$wpdb->prefix}wc_stock_notifications",
2180 "{$wpdb->prefix}wc_stock_notificationmeta",
2181 "{$wpdb->prefix}wc_email_unsubscribes",
2182
2183 // WCA Tables.
2184 "{$wpdb->prefix}wc_order_stats",
2185 "{$wpdb->prefix}wc_order_product_lookup",
2186 "{$wpdb->prefix}wc_order_tax_lookup",
2187 "{$wpdb->prefix}wc_order_coupon_lookup",
2188 "{$wpdb->prefix}wc_admin_notes",
2189 "{$wpdb->prefix}wc_admin_note_actions",
2190 "{$wpdb->prefix}wc_customer_lookup",
2191 "{$wpdb->prefix}wc_category_lookup",
2192 "{$wpdb->prefix}wc_order_fulfillments",
2193 "{$wpdb->prefix}wc_order_fulfillment_meta",
2194
2195 // HPOS.
2196 "{$wpdb->prefix}wc_orders",
2197 "{$wpdb->prefix}wc_order_addresses",
2198 "{$wpdb->prefix}wc_order_operational_data",
2199 "{$wpdb->prefix}wc_orders_meta",
2200 );
2201
2202 /**
2203 * Filter the list of known WooCommerce tables.
2204 *
2205 * If WooCommerce plugins need to add new tables, they can inject them here.
2206 *
2207 * @param array $tables An array of WooCommerce-specific database table names.
2208 * @since 3.4.0
2209 */
2210 $tables = apply_filters( 'woocommerce_install_get_tables', $tables );
2211
2212 return $tables;
2213 }
2214
2215 /**
2216 * Drop WooCommerce tables.
2217 *
2218 * @return void
2219 */
2220 public static function drop_tables() {
2221 global $wpdb;
2222
2223 $tables = self::get_tables();
2224
2225 foreach ( $tables as $table ) {
2226 // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
2227 $wpdb->query( "DROP TABLE IF EXISTS {$table}" );
2228 // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
2229 }
2230 }
2231
2232 /**
2233 * Uninstall tables when MU blog is deleted.
2234 *
2235 * @param array $tables List of tables that will be deleted by WP.
2236 *
2237 * @return string[]
2238 */
2239 public static function wpmu_drop_tables( $tables ) {
2240 return array_merge( $tables, self::get_tables() );
2241 }
2242
2243 /**
2244 * Get the list of Action Scheduler database tables.
2245 *
2246 * These are intentionally kept out of get_tables(): Action Scheduler is a shared library that
2247 * may be bundled by other active plugins, so its tables are only dropped during a full uninstall
2248 * when the site owner explicitly opts in by setting the WC_REMOVE_ACTION_SCHEDULER constant.
2249 *
2250 * @since 11.0.0
2251 *
2252 * @return string[] Action Scheduler table names.
2253 */
2254 public static function get_action_scheduler_tables() {
2255 global $wpdb;
2256
2257 return array(
2258 "{$wpdb->prefix}actionscheduler_actions",
2259 "{$wpdb->prefix}actionscheduler_claims",
2260 "{$wpdb->prefix}actionscheduler_groups",
2261 "{$wpdb->prefix}actionscheduler_logs",
2262 );
2263 }
2264
2265 /**
2266 * Create roles and capabilities.
2267 *
2268 * @return void
2269 */
2270 public static function create_roles() {
2271 global $wp_roles;
2272
2273 if ( ! class_exists( 'WP_Roles' ) ) {
2274 return;
2275 }
2276
2277 if ( ! isset( $wp_roles ) ) {
2278 $wp_roles = new WP_Roles(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
2279 }
2280
2281 // Dummy gettext calls to get strings in the catalog.
2282 /* translators: user role */
2283 _x( 'Customer', 'User role', 'woocommerce' );
2284 /* translators: user role */
2285 _x( 'Shop manager', 'User role', 'woocommerce' );
2286
2287 // Customer role.
2288 add_role(
2289 'customer',
2290 'Customer',
2291 array(
2292 'read' => true,
2293 )
2294 );
2295
2296 // Shop manager role.
2297 add_role(
2298 'shop_manager',
2299 'Shop manager',
2300 array(
2301 'level_9' => true,
2302 'level_8' => true,
2303 'level_7' => true,
2304 'level_6' => true,
2305 'level_5' => true,
2306 'level_4' => true,
2307 'level_3' => true,
2308 'level_2' => true,
2309 'level_1' => true,
2310 'level_0' => true,
2311 'read' => true,
2312 'read_private_pages' => true,
2313 'read_private_posts' => true,
2314 'edit_posts' => true,
2315 'edit_pages' => true,
2316 'edit_published_posts' => true,
2317 'edit_published_pages' => true,
2318 'edit_private_pages' => true,
2319 'edit_private_posts' => true,
2320 'edit_others_posts' => true,
2321 'edit_others_pages' => true,
2322 'publish_posts' => true,
2323 'publish_pages' => true,
2324 'delete_posts' => true,
2325 'delete_pages' => true,
2326 'delete_private_pages' => true,
2327 'delete_private_posts' => true,
2328 'delete_published_pages' => true,
2329 'delete_published_posts' => true,
2330 'delete_others_posts' => true,
2331 'delete_others_pages' => true,
2332 'manage_categories' => true,
2333 'manage_links' => true,
2334 'moderate_comments' => true,
2335 'upload_files' => true,
2336 'export' => true,
2337 'import' => true,
2338 'list_users' => true,
2339 'edit_theme_options' => true,
2340 )
2341 );
2342
2343 $capabilities = self::get_core_capabilities();
2344
2345 foreach ( $capabilities as $cap_group ) {
2346 foreach ( $cap_group as $cap ) {
2347 $wp_roles->add_cap( 'shop_manager', $cap );
2348 $wp_roles->add_cap( 'administrator', $cap );
2349 }
2350 }
2351 }
2352
2353 /**
2354 * Get capabilities for WooCommerce - these are assigned to admin/shop manager during installation or reset.
2355 *
2356 * @return array
2357 */
2358 public static function get_core_capabilities() {
2359 $capabilities = array();
2360
2361 $capabilities['core'] = array(
2362 'manage_woocommerce',
2363 'create_customers',
2364 'view_woocommerce_reports',
2365 );
2366
2367 $capability_types = array( 'product', 'shop_order', 'shop_coupon' );
2368
2369 foreach ( $capability_types as $capability_type ) {
2370
2371 $capabilities[ $capability_type ] = array(
2372 // Post type.
2373 "edit_{$capability_type}",
2374 "read_{$capability_type}",
2375 "delete_{$capability_type}",
2376 "edit_{$capability_type}s",
2377 "edit_others_{$capability_type}s",
2378 "publish_{$capability_type}s",
2379 "read_private_{$capability_type}s",
2380 "delete_{$capability_type}s",
2381 "delete_private_{$capability_type}s",
2382 "delete_published_{$capability_type}s",
2383 "delete_others_{$capability_type}s",
2384 "edit_private_{$capability_type}s",
2385 "edit_published_{$capability_type}s",
2386
2387 // Terms.
2388 "manage_{$capability_type}_terms",
2389 "edit_{$capability_type}_terms",
2390 "delete_{$capability_type}_terms",
2391 "assign_{$capability_type}_terms",
2392 );
2393 }
2394
2395 return $capabilities;
2396 }
2397
2398 /**
2399 * Remove WooCommerce roles.
2400 *
2401 * @return void
2402 */
2403 public static function remove_roles() {
2404 global $wp_roles;
2405
2406 if ( ! class_exists( 'WP_Roles' ) ) {
2407 return;
2408 }
2409
2410 if ( ! isset( $wp_roles ) ) {
2411 $wp_roles = new WP_Roles(); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
2412 }
2413
2414 $capabilities = self::get_core_capabilities();
2415
2416 foreach ( $capabilities as $cap_group ) {
2417 foreach ( $cap_group as $cap ) {
2418 $wp_roles->remove_cap( 'shop_manager', $cap );
2419 $wp_roles->remove_cap( 'administrator', $cap );
2420 }
2421 }
2422
2423 remove_role( 'customer' );
2424 remove_role( 'shop_manager' );
2425 }
2426
2427 /**
2428 * Create files/directories.
2429 *
2430 * @return void
2431 */
2432 private static function create_files() {
2433 /**
2434 * Bypass if filesystem is read-only and/or non-standard upload system is used.
2435 *
2436 * @since 3.2.0
2437 */
2438 if ( apply_filters( 'woocommerce_install_skip_create_files', false ) ) {
2439 return;
2440 }
2441
2442 // Install files and folders for uploading files and prevent hotlinking.
2443 $upload_dir = wp_get_upload_dir();
2444 $download_method = get_option( 'woocommerce_file_download_method', 'force' );
2445
2446 $files = array(
2447 array(
2448 'base' => $upload_dir['basedir'] . '/woocommerce_uploads',
2449 'file' => 'index.html',
2450 'content' => '',
2451 ),
2452 array(
2453 'base' => $upload_dir['basedir'] . '/woocommerce_uploads',
2454 'file' => '.htaccess',
2455 'content' => 'redirect' === $download_method ? 'Options -Indexes' : 'deny from all',
2456 ),
2457 );
2458
2459 foreach ( $files as $file ) {
2460 if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
2461 $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'wb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen
2462 if ( $file_handle ) {
2463 fwrite( $file_handle, $file['content'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
2464 fclose( $file_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
2465 }
2466 }
2467 }
2468
2469 // Create attachment for placeholders.
2470 self::create_placeholder_image();
2471 }
2472
2473 /**
2474 * Create a placeholder image in the media library.
2475 *
2476 * @since 3.5.0
2477 *
2478 * @return void
2479 */
2480 private static function create_placeholder_image() {
2481 $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 );
2482
2483 // Validate current setting if set. If set, return.
2484 if ( ! empty( $placeholder_image ) ) {
2485 if ( ! is_numeric( $placeholder_image ) ) {
2486 return;
2487 } elseif ( $placeholder_image && wp_attachment_is_image( $placeholder_image ) ) {
2488 return;
2489 }
2490 }
2491
2492 $upload_dir = wp_upload_dir();
2493 $source = WC()->plugin_path() . '/assets/images/placeholder-attachment.webp';
2494 $filename = $upload_dir['basedir'] . '/woocommerce-placeholder.webp';
2495
2496 if ( ! file_exists( $filename ) ) {
2497 copy( $source, $filename ); // @codingStandardsIgnoreLine.
2498 }
2499
2500 if ( ! file_exists( $filename ) ) {
2501 update_option( 'woocommerce_placeholder_image', 0 );
2502 return;
2503 }
2504
2505 $filetype = wp_check_filetype( basename( $filename ), null );
2506 $attachment = array(
2507 'guid' => $upload_dir['url'] . '/' . basename( $filename ),
2508 'post_mime_type' => $filetype['type'],
2509 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $filename ) ),
2510 'post_content' => '',
2511 'post_status' => 'inherit',
2512 );
2513
2514 $attach_id = wp_insert_attachment( $attachment, $filename );
2515 if ( is_wp_error( $attach_id ) ) {
2516 update_option( 'woocommerce_placeholder_image', 0 );
2517 return;
2518 }
2519
2520 update_option( 'woocommerce_placeholder_image', $attach_id );
2521
2522 // Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
2523 require_once ABSPATH . 'wp-admin/includes/image.php';
2524
2525 // Generate the metadata for the attachment, and update the database record.
2526 $attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
2527 wp_update_attachment_metadata( $attach_id, $attach_data );
2528 }
2529
2530 /**
2531 * Delete the placeholder image created by create_placeholder_image().
2532 *
2533 * Removes the attachment post, its metadata and the underlying file, but only when the stored
2534 * woocommerce_placeholder_image option still points at WooCommerce's own generated placeholder.
2535 * A custom image set by the merchant through the "Placeholder image" setting is left untouched to
2536 * avoid deleting merchant-owned media. The option itself is removed along with the rest of the
2537 * woocommerce_ options during uninstall.
2538 *
2539 * @since 11.0.0
2540 *
2541 * @return void
2542 */
2543 public static function delete_placeholder_image() {
2544 $placeholder_image = absint( get_option( 'woocommerce_placeholder_image', 0 ) );
2545
2546 if ( ! $placeholder_image ) {
2547 return;
2548 }
2549
2550 // Only delete WooCommerce's own generated placeholder, never a custom image the merchant may have set.
2551 $attached_file = (string) get_post_meta( $placeholder_image, '_wp_attached_file', true );
2552 if ( 'woocommerce-placeholder.webp' !== wp_basename( $attached_file ) ) {
2553 return;
2554 }
2555
2556 wp_delete_attachment( $placeholder_image, true );
2557 }
2558
2559 /**
2560 * Show action links on the plugin screen.
2561 *
2562 * @param mixed $links Plugin Action links.
2563 *
2564 * @return array
2565 */
2566 public static function plugin_action_links( $links ) {
2567 $action_links = array(
2568 'settings' => '<a href="' . admin_url( 'admin.php?page=wc-settings' ) . '" aria-label="' . esc_attr__( 'View WooCommerce settings', 'woocommerce' ) . '">' . esc_html__( 'Settings', 'woocommerce' ) . '</a>',
2569 );
2570
2571 return array_merge( $action_links, $links );
2572 }
2573
2574 /**
2575 * Show row meta on the plugin screen.
2576 *
2577 * @param mixed $links Plugin Row Meta.
2578 * @param mixed $file Plugin Base file.
2579 *
2580 * @return array
2581 */
2582 public static function plugin_row_meta( $links, $file ) {
2583 if ( WC_PLUGIN_BASENAME !== $file ) {
2584 return $links;
2585 }
2586
2587 /**
2588 * The WooCommerce documentation URL.
2589 *
2590 * @since 2.7.0
2591 */
2592 $docs_url = apply_filters( 'woocommerce_docs_url', 'https://woocommerce.com/documentation/plugins/woocommerce/' );
2593
2594 /**
2595 * The WooCommerce API documentation URL.
2596 *
2597 * @since 2.2.0
2598 */
2599 $api_docs_url = apply_filters( 'woocommerce_apidocs_url', 'https://woocommerce.com/wc-apidocs/' );
2600
2601 /**
2602 * The community WooCommerce support URL.
2603 *
2604 * @since 2.2.0
2605 */
2606 $community_support_url = apply_filters( 'woocommerce_community_support_url', 'https://wordpress.org/support/plugin/woocommerce/' );
2607
2608 /**
2609 * The premium support URL.
2610 *
2611 * @since 6.7.0
2612 */
2613 $support_url = apply_filters( 'woocommerce_support_url', 'https://woocommerce.com/my-account/create-a-ticket/' );
2614
2615 $row_meta = array(
2616 'docs' => '<a href="' . esc_url( $docs_url ) . '" aria-label="' . esc_attr__( 'View WooCommerce documentation', 'woocommerce' ) . '">' . esc_html__( 'Docs', 'woocommerce' ) . '</a>',
2617 'apidocs' => '<a href="' . esc_url( $api_docs_url ) . '" aria-label="' . esc_attr__( 'View WooCommerce API docs', 'woocommerce' ) . '">' . esc_html__( 'API docs', 'woocommerce' ) . '</a>',
2618 'support' => '<a href="' . esc_url( $community_support_url ) . '" aria-label="' . esc_attr__( 'Visit community forums', 'woocommerce' ) . '">' . esc_html__( 'Community support', 'woocommerce' ) . '</a>',
2619 );
2620
2621 if ( WCConnectionHelper::is_connected() ) {
2622 $row_meta['premium_support'] = '<a href="' . esc_url( $support_url ) . '" aria-label="' . esc_attr__( 'Visit premium customer support', 'woocommerce' ) . '">' . esc_html__( 'Premium support', 'woocommerce' ) . '</a>';
2623 }
2624
2625 return array_merge( $links, $row_meta );
2626 }
2627
2628 /**
2629 * Get slug from path and associate it with the path.
2630 *
2631 * @param array $plugins Associative array of plugin files to paths.
2632 * @param string $key Plugin relative path. Example: woocommerce/woocommerce.php.
2633 *
2634 * @return array
2635 */
2636 private static function associate_plugin_file( $plugins, $key ) {
2637 $path = explode( '/', $key );
2638 $filename = end( $path );
2639 $plugins[ $filename ] = $key;
2640 return $plugins;
2641 }
2642
2643 /**
2644 * Install a plugin from .org in the background via a cron job (used by
2645 * installer - opt in).
2646 *
2647 * @param string $plugin_to_install_id Plugin ID.
2648 * @param array $plugin_to_install Plugin information.
2649 *
2650 * @throws Exception If unable to proceed with plugin installation.
2651 * @since 2.6.0
2652 * @deprecated 11.1.0 No longer used.
2653 *
2654 * @return void
2655 */
2656 public static function background_installer( $plugin_to_install_id, $plugin_to_install ) {
2657 wc_deprecated_function( 'WC_Install::background_installer', '11.1.0' );
2658
2659 // Explicitly clear the event.
2660 $args = func_get_args();
2661
2662 if ( ! empty( $plugin_to_install['repo-slug'] ) ) {
2663 require_once ABSPATH . 'wp-admin/includes/file.php';
2664 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
2665 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
2666 require_once ABSPATH . 'wp-admin/includes/plugin.php';
2667
2668 WP_Filesystem();
2669
2670 $skin = new Automatic_Upgrader_Skin();
2671 $upgrader = new WP_Upgrader( $skin );
2672 $installed_plugins = array_reduce( array_keys( get_plugins() ), array( __CLASS__, 'associate_plugin_file' ) );
2673 if ( empty( $installed_plugins ) ) {
2674 $installed_plugins = array();
2675 }
2676 $plugin_slug = $plugin_to_install['repo-slug'];
2677 $plugin_file = isset( $plugin_to_install['file'] ) ? $plugin_to_install['file'] : $plugin_slug . '.php';
2678 $installed = false;
2679 $activate = false;
2680
2681 // See if the plugin is installed already.
2682 if ( isset( $installed_plugins[ $plugin_file ] ) ) {
2683 $installed = true;
2684 $activate = ! is_plugin_active( $installed_plugins[ $plugin_file ] );
2685 }
2686
2687 // Install this thing!
2688 if ( ! $installed ) {
2689 // Suppress feedback.
2690 ob_start();
2691
2692 try {
2693 $plugin_information = plugins_api(
2694 'plugin_information',
2695 array(
2696 'slug' => $plugin_slug,
2697 'fields' => array(
2698 'short_description' => false,
2699 'sections' => false,
2700 'requires' => false,
2701 'rating' => false,
2702 'ratings' => false,
2703 'downloaded' => false,
2704 'last_updated' => false,
2705 'added' => false,
2706 'tags' => false,
2707 'homepage' => false,
2708 'donate_link' => false,
2709 'author_profile' => false,
2710 'author' => false,
2711 ),
2712 )
2713 );
2714
2715 if ( is_wp_error( $plugin_information ) ) {
2716 throw new Exception( $plugin_information->get_error_message() );
2717 }
2718
2719 $package = $plugin_information->download_link;
2720 $download = $upgrader->download_package( $package );
2721
2722 if ( is_wp_error( $download ) ) {
2723 throw new Exception( $download->get_error_message() );
2724 }
2725
2726 $working_dir = $upgrader->unpack_package( $download, true );
2727
2728 if ( is_wp_error( $working_dir ) ) {
2729 throw new Exception( $working_dir->get_error_message() );
2730 }
2731
2732 $result = $upgrader->install_package(
2733 array(
2734 'source' => $working_dir,
2735 'destination' => WP_PLUGIN_DIR,
2736 'clear_destination' => false,
2737 'abort_if_destination_exists' => false,
2738 'clear_working' => true,
2739 'hook_extra' => array(
2740 'type' => 'plugin',
2741 'action' => 'install',
2742 ),
2743 )
2744 );
2745
2746 if ( is_wp_error( $result ) ) {
2747 throw new Exception( $result->get_error_message() );
2748 }
2749
2750 $activate = true;
2751
2752 } catch ( Exception $e ) {
2753 WC_Admin_Notices::add_custom_notice(
2754 $plugin_to_install_id . '_install_error',
2755 sprintf(
2756 // translators: 1: plugin name, 2: error message, 3: URL to install plugin manually.
2757 __( '%1$s could not be installed (%2$s). <a href="%3$s">Please install it manually by clicking here.</a>', 'woocommerce' ),
2758 $plugin_to_install['name'],
2759 $e->getMessage(),
2760 esc_url(
2761 wp_nonce_url(
2762 admin_url( 'index.php?wc-install-plugin-redirect=' . $plugin_slug ),
2763 'wc-install-plugin-redirect_' . $plugin_slug
2764 )
2765 )
2766 )
2767 );
2768 }
2769
2770 // Discard feedback.
2771 ob_end_clean();
2772 }
2773
2774 wp_clean_plugins_cache();
2775
2776 // Activate this thing.
2777 if ( $activate ) {
2778 try {
2779 add_action( 'add_option_mailchimp_woocommerce_plugin_do_activation_redirect', array( __CLASS__, 'remove_mailchimps_redirect' ), 10, 2 );
2780 $result = activate_plugin( $installed ? $installed_plugins[ $plugin_file ] : $plugin_slug . '/' . $plugin_file );
2781
2782 if ( is_wp_error( $result ) ) {
2783 throw new Exception( $result->get_error_message() );
2784 }
2785 } catch ( Exception $e ) {
2786 WC_Admin_Notices::add_custom_notice(
2787 $plugin_to_install_id . '_install_error',
2788 sprintf(
2789 // translators: 1: plugin name, 2: URL to WP plugin page.
2790 __( '%1$s was installed but could not be activated. <a href="%2$s">Please activate it manually by clicking here.</a>', 'woocommerce' ),
2791 $plugin_to_install['name'],
2792 admin_url( 'plugins.php' )
2793 )
2794 );
2795 }
2796 }
2797 }
2798 }
2799
2800 /**
2801 * Removes redirect added during MailChimp plugin's activation.
2802 *
2803 * @param string $option Option name.
2804 * @param string $value Option value.
2805 *
2806 * @return void
2807 */
2808 public static function remove_mailchimps_redirect( $option, $value ) {
2809 // Remove this action to prevent infinite looping.
2810 remove_action( 'add_option_mailchimp_woocommerce_plugin_do_activation_redirect', array( __CLASS__, 'remove_mailchimps_redirect' ) );
2811
2812 // Update redirect back to false.
2813 update_option( 'mailchimp_woocommerce_plugin_do_activation_redirect', false );
2814 }
2815
2816 /**
2817 * Install a theme from .org in the background via a cron job (used by installer - opt in).
2818 *
2819 * @param string $theme_slug Theme slug.
2820 *
2821 * @throws Exception If unable to proceed with theme installation.
2822 * @since 3.1.0
2823 * @deprecated 11.1.0 No longer used.
2824 *
2825 * @return void
2826 */
2827 public static function theme_background_installer( $theme_slug ) {
2828 wc_deprecated_function( 'WC_Install::theme_background_installer', '11.1.0' );
2829
2830 // Explicitly clear the event.
2831 $args = func_get_args();
2832
2833 if ( ! empty( $theme_slug ) ) {
2834 // Suppress feedback.
2835 ob_start();
2836
2837 try {
2838 $theme = wp_get_theme( $theme_slug );
2839
2840 if ( ! $theme->exists() ) {
2841 require_once ABSPATH . 'wp-admin/includes/file.php';
2842 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
2843 include_once ABSPATH . 'wp-admin/includes/theme.php';
2844
2845 WP_Filesystem();
2846
2847 $skin = new Automatic_Upgrader_Skin();
2848 $upgrader = new Theme_Upgrader( $skin );
2849 $api = themes_api(
2850 'theme_information',
2851 array(
2852 'slug' => $theme_slug,
2853 'fields' => array( 'sections' => false ),
2854 )
2855 );
2856 $result = $upgrader->install( $api->download_link );
2857
2858 if ( is_wp_error( $result ) ) {
2859 throw new Exception( $result->get_error_message() );
2860 } elseif ( is_wp_error( $skin->result ) ) {
2861 throw new Exception( $skin->result->get_error_message() );
2862 } elseif ( is_null( $result ) ) {
2863 throw new Exception( 'Unable to connect to the filesystem. Please confirm your credentials.' );
2864 }
2865 }
2866
2867 switch_theme( $theme_slug );
2868 } catch ( Exception $e ) {
2869 WC_Admin_Notices::add_custom_notice(
2870 $theme_slug . '_install_error',
2871 sprintf(
2872 // translators: 1: theme slug, 2: error message, 3: URL to install theme manually.
2873 __( '%1$s could not be installed (%2$s). <a href="%3$s">Please install it manually by clicking here.</a>', 'woocommerce' ),
2874 $theme_slug,
2875 $e->getMessage(),
2876 esc_url( admin_url( 'update.php?action=install-theme&theme=' . $theme_slug . '&_wpnonce=' . wp_create_nonce( 'install-theme_' . $theme_slug ) ) )
2877 )
2878 );
2879 }
2880
2881 // Discard feedback.
2882 ob_end_clean();
2883 }
2884 }
2885
2886 /**
2887 * Sets whether PayPal Standard will be loaded on install.
2888 *
2889 * @since 5.5.0
2890 *
2891 * @return void
2892 */
2893 private static function set_paypal_standard_load_eligibility() {
2894 // Initiating the payment gateways sets the flag.
2895 if ( class_exists( 'WC_Gateway_Paypal' ) ) {
2896 WC_Gateway_Paypal::get_instance()->should_load();
2897 }
2898 }
2899
2900 /**
2901 * Gets the content of the sample refunds and return policy page.
2902 *
2903 * @since 5.6.0
2904 * @return string The content for the page
2905 */
2906 private static function get_refunds_return_policy_page_content() {
2907 return <<<'EOT'
2908 <!-- wp:paragraph -->
2909 <p><b>This is a sample page.</b></p>
2910 <!-- /wp:paragraph -->
2911
2912 <!-- wp:heading -->
2913 <h2 class="wp-block-heading">Overview</h2>
2914 <!-- /wp:heading -->
2915
2916 <!-- wp:paragraph -->
2917 <p>Our refund and returns policy lasts 30 days. If 30 days have passed since your purchase, we can’t offer you a full refund or exchange.</p>
2918 <!-- /wp:paragraph -->
2919
2920 <!-- wp:paragraph -->
2921 <p>To be eligible for a return, your item must be unused and in the same condition that you received it. It must also be in the original packaging.</p>
2922 <!-- /wp:paragraph -->
2923
2924 <!-- wp:paragraph -->
2925 <p>Several types of goods are exempt from being returned. Perishable goods such as food, flowers, newspapers or magazines cannot be returned. We also do not accept products that are intimate or sanitary goods, hazardous materials, or flammable liquids or gases.</p>
2926 <!-- /wp:paragraph -->
2927
2928 <!-- wp:paragraph -->
2929 <p>Additional non-returnable items:</p>
2930 <!-- /wp:paragraph -->
2931
2932 <!-- wp:list -->
2933 <ul>
2934 <li>Gift cards</li>
2935 <li>Downloadable software products</li>
2936 <li>Some health and personal care items</li>
2937 </ul>
2938 <!-- /wp:list -->
2939
2940 <!-- wp:paragraph -->
2941 <p>To complete your return, we require a receipt or proof of purchase.</p>
2942 <!-- /wp:paragraph -->
2943
2944 <!-- wp:paragraph -->
2945 <p>Please do not send your purchase back to the manufacturer.</p>
2946 <!-- /wp:paragraph -->
2947
2948 <!-- wp:paragraph -->
2949 <p>There are certain situations where only partial refunds are granted:</p>
2950 <!-- /wp:paragraph -->
2951
2952 <!-- wp:list -->
2953 <ul>
2954 <li>Book with obvious signs of use</li>
2955 <li>CD, DVD, VHS tape, software, video game, cassette tape, or vinyl record that has been opened.</li>
2956 <li>Any item not in its original condition, is damaged or missing parts for reasons not due to our error.</li>
2957 <li>Any item that is returned more than 30 days after delivery</li>
2958 </ul>
2959 <!-- /wp:list -->
2960
2961 <!-- wp:heading -->
2962 <h2 class="wp-block-heading">Refunds</h2>
2963 <!-- /wp:heading -->
2964
2965 <!-- wp:paragraph -->
2966 <p>Once your return is received and inspected, we will send you an email to notify you that we have received your returned item. We will also notify you of the approval or rejection of your refund.</p>
2967 <!-- /wp:paragraph -->
2968
2969 <!-- wp:paragraph -->
2970 <p>If you are approved, then your refund will be processed, and a credit will automatically be applied to your credit card or original method of payment, within a certain amount of days.</p>
2971 <!-- /wp:paragraph -->
2972
2973 <!-- wp:heading {"level":3} -->
2974 <h3 class="wp-block-heading">Late or missing refunds</h3>
2975 <!-- /wp:heading -->
2976
2977 <!-- wp:paragraph -->
2978 <p>If you haven’t received a refund yet, first check your bank account again.</p>
2979 <!-- /wp:paragraph -->
2980
2981 <!-- wp:paragraph -->
2982 <p>Then contact your credit card company, it may take some time before your refund is officially posted.</p>
2983 <!-- /wp:paragraph -->
2984
2985 <!-- wp:paragraph -->
2986 <p>Next contact your bank. There is often some processing time before a refund is posted.</p>
2987 <!-- /wp:paragraph -->
2988
2989 <!-- wp:paragraph -->
2990 <p>If you’ve done all of this and you still have not received your refund yet, please contact us at {email address}.</p>
2991 <!-- /wp:paragraph -->
2992
2993 <!-- wp:heading {"level":3} -->
2994 <h3 class="wp-block-heading">Sale items</h3>
2995 <!-- /wp:heading -->
2996
2997 <!-- wp:paragraph -->
2998 <p>Only regular priced items may be refunded. Sale items cannot be refunded.</p>
2999 <!-- /wp:paragraph -->
3000
3001 <!-- wp:heading -->
3002 <h2 class="wp-block-heading">Exchanges</h2>
3003 <!-- /wp:heading -->
3004
3005 <!-- wp:paragraph -->
3006 <p>We only replace items if they are defective or damaged. If you need to exchange it for the same item, send us an email at {email address} and send your item to: {physical address}.</p>
3007 <!-- /wp:paragraph -->
3008
3009 <!-- wp:heading -->
3010 <h2 class="wp-block-heading">Gifts</h2>
3011 <!-- /wp:heading -->
3012
3013 <!-- wp:paragraph -->
3014 <p>If the item was marked as a gift when purchased and shipped directly to you, you’ll receive a gift credit for the value of your return. Once the returned item is received, a gift certificate will be mailed to you.</p>
3015 <!-- /wp:paragraph -->
3016
3017 <!-- wp:paragraph -->
3018 <p>If the item wasn’t marked as a gift when purchased, or the gift giver had the order shipped to themselves to give to you later, we will send a refund to the gift giver and they will find out about your return.</p>
3019 <!-- /wp:paragraph -->
3020
3021 <!-- wp:heading -->
3022 <h2 class="wp-block-heading">Shipping returns</h2>
3023 <!-- /wp:heading -->
3024
3025 <!-- wp:paragraph -->
3026 <p>To return your product, you should mail your product to: {physical address}.</p>
3027 <!-- /wp:paragraph -->
3028
3029 <!-- wp:paragraph -->
3030 <p>You will be responsible for paying for your own shipping costs for returning your item. Shipping costs are non-refundable. If you receive a refund, the cost of return shipping will be deducted from your refund.</p>
3031 <!-- /wp:paragraph -->
3032
3033 <!-- wp:paragraph -->
3034 <p>Depending on where you live, the time it may take for your exchanged product to reach you may vary.</p>
3035 <!-- /wp:paragraph -->
3036
3037 <!-- wp:paragraph -->
3038 <p>If you are returning more expensive items, you may consider using a trackable shipping service or purchasing shipping insurance. We don’t guarantee that we will receive your returned item.</p>
3039 <!-- /wp:paragraph -->
3040
3041 <!-- wp:heading -->
3042 <h2 class="wp-block-heading">Need help?</h2>
3043 <!-- /wp:heading -->
3044
3045 <!-- wp:paragraph -->
3046 <p>Contact us at {email} for questions related to refunds and returns.</p>
3047 <!-- /wp:paragraph -->
3048 EOT;
3049 }
3050
3051 /**
3052 * Adds an admin inbox note after a page has been created to notify
3053 * user. For example to take action to edit the page such as the
3054 * Refund and returns page.
3055 *
3056 * @since 5.6.0
3057 * @deprecated 10.5.0 No longer used.
3058 *
3059 * @return void
3060 */
3061 public static function add_admin_note_after_page_created() {
3062 wc_deprecated_function( 'WC_Install::add_admin_note_after_page_created', '10.5.0' );
3063
3064 if ( ! WC()->is_wc_admin_active() ) {
3065 return;
3066 }
3067
3068 $page_id = get_option( 'woocommerce_refund_returns_page_created', null );
3069
3070 if ( null === $page_id ) {
3071 return;
3072 }
3073
3074 WC_Notes_Refund_Returns::possibly_add_note( $page_id );
3075 }
3076
3077 /**
3078 * When pages are created, we might want to take some action.
3079 * In this case we want to create an admin note when the refund and returns
3080 * page is created.
3081 *
3082 * @since 5.6.0
3083 * @param int $page_id ID of the page.
3084 * @param array $page_data The data of the page created.
3085 * @return void
3086 */
3087 public static function page_created( $page_id, $page_data ) {
3088 if ( Constants::is_true( 'WC_INSTALLING' ) ) {
3089 return;
3090 }
3091
3092 if ( 'refund_returns' === $page_data['post_name'] && class_exists( 'WC_Notes_Refund_Returns', false ) ) {
3093 $callback = fn() => WC_Notes_Refund_Returns::possibly_add_note( $page_id );
3094
3095 if ( did_action( 'init' ) ) {
3096 $callback();
3097 } else {
3098 add_action( 'init', $callback );
3099 }
3100 }
3101 }
3102
3103 /**
3104 * Get the Cart block content.
3105 *
3106 * @since 8.3.0
3107 * @return string
3108 */
3109 protected static function get_cart_block_content() {
3110 return '<!-- wp:woocommerce/cart -->
3111 <div class="wp-block-woocommerce-cart alignwide is-loading"><!-- wp:woocommerce/filled-cart-block -->
3112 <div class="wp-block-woocommerce-filled-cart-block"><!-- wp:woocommerce/cart-items-block -->
3113 <div class="wp-block-woocommerce-cart-items-block"><!-- wp:woocommerce/cart-line-items-block -->
3114 <div class="wp-block-woocommerce-cart-line-items-block"></div>
3115 <!-- /wp:woocommerce/cart-line-items-block -->
3116
3117 <!-- wp:woocommerce/product-collection {"queryId":0,"query":{"perPage":3,"pages":1,"offset":0,"postType":"product","order":"asc","orderBy":"title","search":"","exclude":[],"inherit":false,"taxQuery":{},"isProductCollectionBlock":true,"featured":false,"woocommerceOnSale":false,"woocommerceStockStatus":["instock","outofstock","onbackorder"],"woocommerceAttributes":[],"woocommerceHandPickedProducts":[],"filterable":false,"relatedBy":{"categories":true,"tags":true}},"tagName":"div","displayLayout":{"type":"flex","columns":3,"shrinkColumns":true},"dimensions":{"widthType":"fill"},"collection":"woocommerce/product-collection/cross-sells","hideControls":["filterable"],"queryContextIncludes":["collection"]} -->
3118 <div class="wp-block-woocommerce-product-collection"><!-- wp:heading {"textAlign":"left","style":{"spacing":{"margin":{"bottom":"1rem"}}}} -->
3119 <h2 class="wp-block-heading has-text-align-left" style="margin-bottom:1rem">' . __( 'You may be interested in&hellip;', 'woocommerce' ) . '</h2>
3120
3121 <!-- /wp:heading -->
3122
3123 <!-- wp:woocommerce/product-template -->
3124 <!-- wp:woocommerce/product-image {"showSaleBadge":false,"imageSizing":"thumbnail","isDescendentOfQueryLoop":true} -->
3125 <!-- wp:woocommerce/product-sale-badge {"align":"right"} /-->
3126 <!-- /wp:woocommerce/product-image -->
3127
3128 <!-- wp:post-title {"textAlign":"center","isLink":true,"style":{"spacing":{"margin":{"bottom":"0.75rem","top":"0"}},"typography":{"lineHeight":"1.4"}},"fontSize":"medium","__woocommerceNamespace":"woocommerce/product-collection/product-title"} /-->
3129
3130 <!-- wp:woocommerce/product-price {"isDescendentOfQueryLoop":true,"textAlign":"center","fontSize":"small"} /-->
3131
3132 <!-- wp:woocommerce/product-button {"textAlign":"center","isDescendentOfQueryLoop":true,"fontSize":"small"} /-->
3133 <!-- /wp:woocommerce/product-template --></div>
3134 <!-- /wp:woocommerce/product-collection --></div>
3135
3136 <!-- /wp:woocommerce/cart-items-block -->
3137
3138 <!-- wp:woocommerce/cart-totals-block -->
3139 <div class="wp-block-woocommerce-cart-totals-block"><!-- wp:woocommerce/cart-order-summary-block -->
3140 <div class="wp-block-woocommerce-cart-order-summary-block"><!-- wp:woocommerce/cart-order-summary-heading-block -->
3141 <div class="wp-block-woocommerce-cart-order-summary-heading-block"></div>
3142 <!-- /wp:woocommerce/cart-order-summary-heading-block -->
3143
3144 <!-- wp:woocommerce/cart-order-summary-coupon-form-block -->
3145 <div class="wp-block-woocommerce-cart-order-summary-coupon-form-block"></div>
3146 <!-- /wp:woocommerce/cart-order-summary-coupon-form-block -->
3147
3148 <!-- wp:woocommerce/cart-order-summary-subtotal-block -->
3149 <div class="wp-block-woocommerce-cart-order-summary-subtotal-block"></div>
3150 <!-- /wp:woocommerce/cart-order-summary-subtotal-block -->
3151
3152 <!-- wp:woocommerce/cart-order-summary-fee-block -->
3153 <div class="wp-block-woocommerce-cart-order-summary-fee-block"></div>
3154 <!-- /wp:woocommerce/cart-order-summary-fee-block -->
3155
3156 <!-- wp:woocommerce/cart-order-summary-discount-block -->
3157 <div class="wp-block-woocommerce-cart-order-summary-discount-block"></div>
3158 <!-- /wp:woocommerce/cart-order-summary-discount-block -->
3159
3160 <!-- wp:woocommerce/cart-order-summary-shipping-block -->
3161 <div class="wp-block-woocommerce-cart-order-summary-shipping-block"></div>
3162 <!-- /wp:woocommerce/cart-order-summary-shipping-block -->
3163
3164 <!-- wp:woocommerce/cart-order-summary-taxes-block -->
3165 <div class="wp-block-woocommerce-cart-order-summary-taxes-block"></div>
3166 <!-- /wp:woocommerce/cart-order-summary-taxes-block --></div>
3167 <!-- /wp:woocommerce/cart-order-summary-block -->
3168
3169 <!-- wp:woocommerce/cart-express-payment-block -->
3170 <div class="wp-block-woocommerce-cart-express-payment-block"></div>
3171 <!-- /wp:woocommerce/cart-express-payment-block -->
3172
3173 <!-- wp:woocommerce/proceed-to-checkout-block -->
3174 <div class="wp-block-woocommerce-proceed-to-checkout-block"></div>
3175 <!-- /wp:woocommerce/proceed-to-checkout-block -->
3176
3177 <!-- wp:woocommerce/cart-accepted-payment-methods-block -->
3178 <div class="wp-block-woocommerce-cart-accepted-payment-methods-block"></div>
3179 <!-- /wp:woocommerce/cart-accepted-payment-methods-block --></div>
3180 <!-- /wp:woocommerce/cart-totals-block --></div>
3181 <!-- /wp:woocommerce/filled-cart-block -->
3182
3183 <!-- wp:woocommerce/empty-cart-block -->
3184 <div class="wp-block-woocommerce-empty-cart-block"><!-- wp:heading {"textAlign":"center","className":"with-empty-cart-icon wc-block-cart__empty-cart__title"} -->
3185 <h2 class="wp-block-heading has-text-align-center with-empty-cart-icon wc-block-cart__empty-cart__title">' . __( 'Your cart is currently empty!', 'woocommerce' ) . '</h2>
3186 <!-- /wp:heading -->
3187
3188 <!-- wp:separator {"className":"is-style-dots"} -->
3189 <hr class="wp-block-separator has-alpha-channel-opacity is-style-dots"/>
3190 <!-- /wp:separator -->
3191
3192 <!-- wp:heading {"textAlign":"center"} -->
3193 <h2 class="wp-block-heading has-text-align-center">' . __( 'New in store', 'woocommerce' ) . '</h2>
3194 <!-- /wp:heading -->
3195
3196 <!-- wp:woocommerce/product-new {"columns":4,"rows":1} /--></div>
3197 <!-- /wp:woocommerce/empty-cart-block --></div>
3198 <!-- /wp:woocommerce/cart -->';
3199 }
3200
3201 /**
3202 * Get the Checkout block content.
3203 *
3204 * @since 8.3.0
3205 * @return string
3206 */
3207 protected static function get_checkout_block_content() {
3208 return '<!-- wp:woocommerce/checkout -->
3209 <div class="wp-block-woocommerce-checkout alignwide wc-block-checkout is-loading"><!-- wp:woocommerce/checkout-fields-block -->
3210 <div class="wp-block-woocommerce-checkout-fields-block"><!-- wp:woocommerce/checkout-express-payment-block -->
3211 <div class="wp-block-woocommerce-checkout-express-payment-block"></div>
3212 <!-- /wp:woocommerce/checkout-express-payment-block -->
3213
3214 <!-- wp:woocommerce/checkout-contact-information-block -->
3215 <div class="wp-block-woocommerce-checkout-contact-information-block"></div>
3216 <!-- /wp:woocommerce/checkout-contact-information-block -->
3217
3218 <!-- wp:woocommerce/checkout-shipping-method-block -->
3219 <div class="wp-block-woocommerce-checkout-shipping-method-block"></div>
3220 <!-- /wp:woocommerce/checkout-shipping-method-block -->
3221
3222 <!-- wp:woocommerce/checkout-pickup-options-block -->
3223 <div class="wp-block-woocommerce-checkout-pickup-options-block"></div>
3224 <!-- /wp:woocommerce/checkout-pickup-options-block -->
3225
3226 <!-- wp:woocommerce/checkout-shipping-address-block -->
3227 <div class="wp-block-woocommerce-checkout-shipping-address-block"></div>
3228 <!-- /wp:woocommerce/checkout-shipping-address-block -->
3229
3230 <!-- wp:woocommerce/checkout-billing-address-block -->
3231 <div class="wp-block-woocommerce-checkout-billing-address-block"></div>
3232 <!-- /wp:woocommerce/checkout-billing-address-block -->
3233
3234 <!-- wp:woocommerce/checkout-shipping-methods-block -->
3235 <div class="wp-block-woocommerce-checkout-shipping-methods-block"></div>
3236 <!-- /wp:woocommerce/checkout-shipping-methods-block -->
3237
3238 <!-- wp:woocommerce/checkout-payment-block -->
3239 <div class="wp-block-woocommerce-checkout-payment-block"></div>
3240 <!-- /wp:woocommerce/checkout-payment-block -->
3241
3242 <!-- wp:woocommerce/checkout-additional-information-block -->
3243 <div class="wp-block-woocommerce-checkout-additional-information-block"></div>
3244 <!-- /wp:woocommerce/checkout-additional-information-block -->
3245
3246 <!-- wp:woocommerce/checkout-order-note-block -->
3247 <div class="wp-block-woocommerce-checkout-order-note-block"></div>
3248 <!-- /wp:woocommerce/checkout-order-note-block -->
3249
3250 <!-- wp:woocommerce/checkout-terms-block -->
3251 <div class="wp-block-woocommerce-checkout-terms-block"></div>
3252 <!-- /wp:woocommerce/checkout-terms-block -->
3253
3254 <!-- wp:woocommerce/checkout-actions-block -->
3255 <div class="wp-block-woocommerce-checkout-actions-block"></div>
3256 <!-- /wp:woocommerce/checkout-actions-block --></div>
3257 <!-- /wp:woocommerce/checkout-fields-block -->
3258
3259 <!-- wp:woocommerce/checkout-totals-block -->
3260 <div class="wp-block-woocommerce-checkout-totals-block"><!-- wp:woocommerce/checkout-order-summary-block -->
3261 <div class="wp-block-woocommerce-checkout-order-summary-block"><!-- wp:woocommerce/checkout-order-summary-cart-items-block -->
3262 <div class="wp-block-woocommerce-checkout-order-summary-cart-items-block"></div>
3263 <!-- /wp:woocommerce/checkout-order-summary-cart-items-block -->
3264
3265 <!-- wp:woocommerce/checkout-order-summary-coupon-form-block -->
3266 <div class="wp-block-woocommerce-checkout-order-summary-coupon-form-block"></div>
3267 <!-- /wp:woocommerce/checkout-order-summary-coupon-form-block -->
3268
3269 <!-- wp:woocommerce/checkout-order-summary-subtotal-block -->
3270 <div class="wp-block-woocommerce-checkout-order-summary-subtotal-block"></div>
3271 <!-- /wp:woocommerce/checkout-order-summary-subtotal-block -->
3272
3273 <!-- wp:woocommerce/checkout-order-summary-fee-block -->
3274 <div class="wp-block-woocommerce-checkout-order-summary-fee-block"></div>
3275 <!-- /wp:woocommerce/checkout-order-summary-fee-block -->
3276
3277 <!-- wp:woocommerce/checkout-order-summary-discount-block -->
3278 <div class="wp-block-woocommerce-checkout-order-summary-discount-block"></div>
3279 <!-- /wp:woocommerce/checkout-order-summary-discount-block -->
3280
3281 <!-- wp:woocommerce/checkout-order-summary-shipping-block -->
3282 <div class="wp-block-woocommerce-checkout-order-summary-shipping-block"></div>
3283 <!-- /wp:woocommerce/checkout-order-summary-shipping-block -->
3284
3285 <!-- wp:woocommerce/checkout-order-summary-taxes-block -->
3286 <div class="wp-block-woocommerce-checkout-order-summary-taxes-block"></div>
3287 <!-- /wp:woocommerce/checkout-order-summary-taxes-block --></div>
3288 <!-- /wp:woocommerce/checkout-order-summary-block --></div>
3289 <!-- /wp:woocommerce/checkout-totals-block --></div>
3290 <!-- /wp:woocommerce/checkout -->';
3291 }
3292 }
3293
3294 WC_Install::init();
3295