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