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