PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 5.9.0
WooCommerce v5.9.0
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 years ago admin 4 years ago blocks 5 years ago cli 4 years ago customizer 4 years ago data-stores 4 years ago emails 4 years ago export 4 years ago gateways 4 years ago import 4 years ago integrations 5 years ago interfaces 4 years ago legacy 5 years ago libraries 6 years ago log-handlers 5 years ago payment-tokens 5 years ago queue 5 years ago rest-api 4 years ago shipping 5 years ago shortcodes 5 years ago theme-support 5 years ago tracks 4 years ago traits 5 years ago walkers 5 years ago wccom-site 5 years ago widgets 4 years ago class-wc-ajax.php 4 years ago class-wc-api.php 5 years ago class-wc-auth.php 5 years ago class-wc-autoloader.php 5 years ago class-wc-background-emailer.php 5 years ago class-wc-background-updater.php 5 years ago class-wc-breadcrumb.php 5 years ago class-wc-cache-helper.php 4 years ago class-wc-cart-fees.php 5 years ago class-wc-cart-session.php 5 years ago class-wc-cart-totals.php 4 years ago class-wc-cart.php 5 years ago class-wc-checkout.php 4 years ago class-wc-cli.php 4 years ago class-wc-comments.php 5 years ago class-wc-countries.php 4 years ago class-wc-coupon.php 5 years ago class-wc-customer-download-log.php 5 years ago class-wc-customer-download.php 5 years ago class-wc-customer.php 4 years ago class-wc-data-exception.php 8 years ago class-wc-data-store.php 6 years ago class-wc-datetime.php 5 years ago class-wc-deprecated-action-hooks.php 8 years ago class-wc-deprecated-filter-hooks.php 7 years ago class-wc-discounts.php 4 years ago class-wc-download-handler.php 4 years ago class-wc-emails.php 5 years ago class-wc-embed.php 5 years ago class-wc-form-handler.php 5 years ago class-wc-frontend-scripts.php 4 years ago class-wc-geo-ip.php 5 years ago class-wc-geolite-integration.php 6 years ago class-wc-geolocation.php 4 years ago class-wc-https.php 5 years ago class-wc-install.php 4 years ago class-wc-integrations.php 5 years ago class-wc-log-levels.php 5 years ago class-wc-logger.php 5 years ago class-wc-meta-data.php 7 years ago class-wc-order-factory.php 5 years ago class-wc-order-item-coupon.php 5 years ago class-wc-order-item-fee.php 5 years ago class-wc-order-item-meta.php 5 years ago class-wc-order-item-product.php 5 years ago class-wc-order-item-shipping.php 5 years ago class-wc-order-item-tax.php 5 years ago class-wc-order-item.php 5 years ago class-wc-order-query.php 4 years ago class-wc-order-refund.php 5 years ago class-wc-order.php 4 years ago class-wc-payment-gateways.php 4 years ago class-wc-payment-tokens.php 5 years ago class-wc-post-data.php 4 years ago class-wc-post-types.php 4 years ago class-wc-privacy-background-process.php 5 years ago class-wc-privacy-erasers.php 4 years ago class-wc-privacy-exporters.php 4 years ago class-wc-privacy.php 4 years ago class-wc-product-attribute.php 5 years ago class-wc-product-download.php 4 years ago class-wc-product-external.php 5 years ago class-wc-product-factory.php 5 years ago class-wc-product-grouped.php 8 years ago class-wc-product-query.php 5 years ago class-wc-product-simple.php 5 years ago class-wc-product-variable.php 4 years ago class-wc-product-variation.php 4 years ago class-wc-query.php 4 years ago class-wc-rate-limiter.php 5 years ago class-wc-regenerate-images-request.php 5 years ago class-wc-regenerate-images.php 5 years ago class-wc-register-wp-admin-settings.php 4 years ago class-wc-rest-authentication.php 5 years ago class-wc-rest-exception.php 5 years ago class-wc-session-handler.php 5 years ago class-wc-shipping-rate.php 5 years ago class-wc-shipping-zone.php 5 years ago class-wc-shipping-zones.php 5 years ago class-wc-shipping.php 5 years ago class-wc-shortcodes.php 5 years ago class-wc-structured-data.php 5 years ago class-wc-tax.php 5 years ago class-wc-template-loader.php 4 years ago class-wc-tracker.php 4 years ago class-wc-validation.php 5 years ago class-wc-webhook.php 5 years ago class-woocommerce.php 4 years ago wc-account-functions.php 5 years ago wc-attribute-functions.php 5 years ago wc-cart-functions.php 5 years ago wc-conditional-functions.php 5 years ago wc-core-functions.php 4 years ago wc-coupon-functions.php 5 years ago wc-deprecated-functions.php 5 years ago wc-formatting-functions.php 4 years ago wc-notice-functions.php 5 years ago wc-order-functions.php 4 years ago wc-order-item-functions.php 5 years ago wc-page-functions.php 5 years ago wc-product-functions.php 4 years ago wc-rest-functions.php 5 years ago wc-stock-functions.php 5 years ago wc-template-functions.php 4 years ago wc-template-hooks.php 5 years ago wc-term-functions.php 5 years ago wc-update-functions.php 4 years ago wc-user-functions.php 5 years ago wc-webhook-functions.php 5 years ago wc-widget-functions.php 5 years ago
class-wc-install.php
1863 lines
1 <?php
2 /**
3 * Installation related functions and actions.
4 *
5 * @package WooCommerce\Classes
6 * @version 3.0.0
7 */
8
9 use Automattic\Jetpack\Constants;
10 use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper as WCConnectionHelper;
11
12 defined( 'ABSPATH' ) || exit;
13
14 /**
15 * WC_Install Class.
16 */
17 class WC_Install {
18
19 /**
20 * DB updates and callbacks that need to be run per version.
21 *
22 * @var array
23 */
24 private static $db_updates = array(
25 '2.0.0' => array(
26 'wc_update_200_file_paths',
27 'wc_update_200_permalinks',
28 'wc_update_200_subcat_display',
29 'wc_update_200_taxrates',
30 'wc_update_200_line_items',
31 'wc_update_200_images',
32 'wc_update_200_db_version',
33 ),
34 '2.0.9' => array(
35 'wc_update_209_brazillian_state',
36 'wc_update_209_db_version',
37 ),
38 '2.1.0' => array(
39 'wc_update_210_remove_pages',
40 'wc_update_210_file_paths',
41 'wc_update_210_db_version',
42 ),
43 '2.2.0' => array(
44 'wc_update_220_shipping',
45 'wc_update_220_order_status',
46 'wc_update_220_variations',
47 'wc_update_220_attributes',
48 'wc_update_220_db_version',
49 ),
50 '2.3.0' => array(
51 'wc_update_230_options',
52 'wc_update_230_db_version',
53 ),
54 '2.4.0' => array(
55 'wc_update_240_options',
56 'wc_update_240_shipping_methods',
57 'wc_update_240_api_keys',
58 'wc_update_240_refunds',
59 'wc_update_240_db_version',
60 ),
61 '2.4.1' => array(
62 'wc_update_241_variations',
63 'wc_update_241_db_version',
64 ),
65 '2.5.0' => array(
66 'wc_update_250_currency',
67 'wc_update_250_db_version',
68 ),
69 '2.6.0' => array(
70 'wc_update_260_options',
71 'wc_update_260_termmeta',
72 'wc_update_260_zones',
73 'wc_update_260_zone_methods',
74 'wc_update_260_refunds',
75 'wc_update_260_db_version',
76 ),
77 '3.0.0' => array(
78 'wc_update_300_grouped_products',
79 'wc_update_300_settings',
80 'wc_update_300_product_visibility',
81 'wc_update_300_db_version',
82 ),
83 '3.1.0' => array(
84 'wc_update_310_downloadable_products',
85 'wc_update_310_old_comments',
86 'wc_update_310_db_version',
87 ),
88 '3.1.2' => array(
89 'wc_update_312_shop_manager_capabilities',
90 'wc_update_312_db_version',
91 ),
92 '3.2.0' => array(
93 'wc_update_320_mexican_states',
94 'wc_update_320_db_version',
95 ),
96 '3.3.0' => array(
97 'wc_update_330_image_options',
98 'wc_update_330_webhooks',
99 'wc_update_330_product_stock_status',
100 'wc_update_330_set_default_product_cat',
101 'wc_update_330_clear_transients',
102 'wc_update_330_set_paypal_sandbox_credentials',
103 'wc_update_330_db_version',
104 ),
105 '3.4.0' => array(
106 'wc_update_340_states',
107 'wc_update_340_state',
108 'wc_update_340_last_active',
109 'wc_update_340_db_version',
110 ),
111 '3.4.3' => array(
112 'wc_update_343_cleanup_foreign_keys',
113 'wc_update_343_db_version',
114 ),
115 '3.4.4' => array(
116 'wc_update_344_recreate_roles',
117 'wc_update_344_db_version',
118 ),
119 '3.5.0' => array(
120 'wc_update_350_reviews_comment_type',
121 'wc_update_350_db_version',
122 ),
123 '3.5.2' => array(
124 'wc_update_352_drop_download_log_fk',
125 ),
126 '3.5.4' => array(
127 'wc_update_354_modify_shop_manager_caps',
128 'wc_update_354_db_version',
129 ),
130 '3.6.0' => array(
131 'wc_update_360_product_lookup_tables',
132 'wc_update_360_term_meta',
133 'wc_update_360_downloadable_product_permissions_index',
134 'wc_update_360_db_version',
135 ),
136 '3.7.0' => array(
137 'wc_update_370_tax_rate_classes',
138 'wc_update_370_mro_std_currency',
139 'wc_update_370_db_version',
140 ),
141 '3.9.0' => array(
142 'wc_update_390_move_maxmind_database',
143 'wc_update_390_change_geolocation_database_update_cron',
144 'wc_update_390_db_version',
145 ),
146 '4.0.0' => array(
147 'wc_update_product_lookup_tables',
148 'wc_update_400_increase_size_of_column',
149 'wc_update_400_reset_action_scheduler_migration_status',
150 'wc_update_400_db_version',
151 ),
152 '4.4.0' => array(
153 'wc_update_440_insert_attribute_terms_for_variable_products',
154 'wc_update_440_db_version',
155 ),
156 '4.5.0' => array(
157 'wc_update_450_sanitize_coupons_code',
158 'wc_update_450_db_version',
159 ),
160 '5.0.0' => array(
161 'wc_update_500_fix_product_review_count',
162 'wc_update_500_db_version',
163 ),
164 '5.6.0' => array(
165 'wc_update_560_create_refund_returns_page',
166 'wc_update_560_db_version',
167 ),
168 );
169
170 /**
171 * Hook in tabs.
172 */
173 public static function init() {
174 add_action( 'init', array( __CLASS__, 'check_version' ), 5 );
175 add_action( 'init', array( __CLASS__, 'manual_database_update' ), 20 );
176 add_action( 'admin_init', array( __CLASS__, 'wc_admin_db_update_notice' ) );
177 add_action( 'admin_init', array( __CLASS__, 'add_admin_note_after_page_created' ) );
178 add_action( 'woocommerce_run_update_callback', array( __CLASS__, 'run_update_callback' ) );
179 add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
180 add_action( 'woocommerce_page_created', array( __CLASS__, 'page_created' ), 10, 2 );
181 add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
182 add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
183 add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
184 add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
185 }
186
187 /**
188 * Check WooCommerce version and run the updater is required.
189 *
190 * This check is done on all requests and runs if the versions do not match.
191 */
192 public static function check_version() {
193 if ( ! Constants::is_defined( 'IFRAME_REQUEST' ) && version_compare( get_option( 'woocommerce_version' ), WC()->version, '<' ) ) {
194 self::install();
195 do_action( 'woocommerce_updated' );
196 }
197 }
198
199 /**
200 * Performan manual database update when triggered by WooCommerce System Tools.
201 *
202 * @since 3.6.5
203 */
204 public static function manual_database_update() {
205 $blog_id = get_current_blog_id();
206
207 add_action( 'wp_' . $blog_id . '_wc_updater_cron', array( __CLASS__, 'run_manual_database_update' ) );
208 }
209
210 /**
211 * Add WC Admin based db update notice.
212 *
213 * @since 4.0.0
214 */
215 public static function wc_admin_db_update_notice() {
216 if (
217 WC()->is_wc_admin_active() &&
218 false !== get_option( 'woocommerce_admin_install_timestamp' )
219 ) {
220 new WC_Notes_Run_Db_Update();
221 }
222 }
223
224 /**
225 * Run manual database update.
226 */
227 public static function run_manual_database_update() {
228 self::update();
229 }
230
231 /**
232 * Run an update callback when triggered by ActionScheduler.
233 *
234 * @param string $update_callback Callback name.
235 *
236 * @since 3.6.0
237 */
238 public static function run_update_callback( $update_callback ) {
239 include_once dirname( __FILE__ ) . '/wc-update-functions.php';
240
241 if ( is_callable( $update_callback ) ) {
242 self::run_update_callback_start( $update_callback );
243 $result = (bool) call_user_func( $update_callback );
244 self::run_update_callback_end( $update_callback, $result );
245 }
246 }
247
248 /**
249 * Triggered when a callback will run.
250 *
251 * @since 3.6.0
252 * @param string $callback Callback name.
253 */
254 protected static function run_update_callback_start( $callback ) {
255 wc_maybe_define_constant( 'WC_UPDATING', true );
256 }
257
258 /**
259 * Triggered when a callback has ran.
260 *
261 * @since 3.6.0
262 * @param string $callback Callback name.
263 * @param bool $result Return value from callback. Non-false need to run again.
264 */
265 protected static function run_update_callback_end( $callback, $result ) {
266 if ( $result ) {
267 WC()->queue()->add(
268 'woocommerce_run_update_callback',
269 array(
270 'update_callback' => $callback,
271 ),
272 'woocommerce-db-updates'
273 );
274 }
275 }
276
277 /**
278 * Install actions when a update button is clicked within the admin area.
279 *
280 * This function is hooked into admin_init to affect admin only.
281 */
282 public static function install_actions() {
283 if ( ! empty( $_GET['do_update_woocommerce'] ) ) { // WPCS: input var ok.
284 check_admin_referer( 'wc_db_update', 'wc_db_update_nonce' );
285 self::update();
286 WC_Admin_Notices::add_notice( 'update', true );
287 }
288 }
289
290 /**
291 * Install WC.
292 */
293 public static function install() {
294 if ( ! is_blog_installed() ) {
295 return;
296 }
297
298 // Check if we are not already running this routine.
299 if ( 'yes' === get_transient( 'wc_installing' ) ) {
300 return;
301 }
302
303 // If we made it till here nothing is running yet, lets set the transient now.
304 set_transient( 'wc_installing', 'yes', MINUTE_IN_SECONDS * 10 );
305 wc_maybe_define_constant( 'WC_INSTALLING', true );
306
307 WC()->wpdb_table_fix();
308 self::remove_admin_notices();
309 self::create_tables();
310 self::verify_base_tables();
311 self::create_options();
312 self::create_roles();
313 self::setup_environment();
314 self::create_terms();
315 self::create_cron_jobs();
316 self::create_files();
317 self::maybe_create_pages();
318 self::maybe_set_activation_transients();
319 self::set_paypal_standard_load_eligibility();
320 self::update_wc_version();
321 self::maybe_update_db_version();
322
323 delete_transient( 'wc_installing' );
324
325 do_action( 'woocommerce_flush_rewrite_rules' );
326 do_action( 'woocommerce_installed' );
327 }
328
329 /**
330 * Check if all the base tables are present.
331 *
332 * @param bool $modify_notice Whether to modify notice based on if all tables are present.
333 * @param bool $execute Whether to execute get_schema queries as well.
334 *
335 * @return array List of querues.
336 */
337 public static function verify_base_tables( $modify_notice = true, $execute = false ) {
338 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
339
340 if ( $execute ) {
341 self::create_tables();
342 }
343 $queries = dbDelta( self::get_schema(), false );
344 $missing_tables = array();
345 foreach ( $queries as $table_name => $result ) {
346 if ( "Created table $table_name" === $result ) {
347 $missing_tables[] = $table_name;
348 }
349 }
350
351 if ( 0 < count( $missing_tables ) ) {
352 if ( $modify_notice ) {
353 WC_Admin_Notices::add_notice( 'base_tables_missing' );
354 }
355 update_option( 'woocommerce_schema_missing_tables', $missing_tables );
356 } else {
357 if ( $modify_notice ) {
358 WC_Admin_Notices::remove_notice( 'base_tables_missing' );
359 }
360 update_option( 'woocommerce_schema_version', WC()->db_version );
361 delete_option( 'woocommerce_schema_missing_tables' );
362 }
363 return $missing_tables;
364 }
365
366 /**
367 * Reset any notices added to admin.
368 *
369 * @since 3.2.0
370 */
371 private static function remove_admin_notices() {
372 include_once dirname( __FILE__ ) . '/admin/class-wc-admin-notices.php';
373 WC_Admin_Notices::remove_all_notices();
374 }
375
376 /**
377 * Setup WC environment - post types, taxonomies, endpoints.
378 *
379 * @since 3.2.0
380 */
381 private static function setup_environment() {
382 WC_Post_types::register_post_types();
383 WC_Post_types::register_taxonomies();
384 WC()->query->init_query_vars();
385 WC()->query->add_endpoints();
386 WC_API::add_endpoint();
387 WC_Auth::add_endpoint();
388 }
389
390 /**
391 * Is this a brand new WC install?
392 *
393 * A brand new install has no version yet. Also treat empty installs as 'new'.
394 *
395 * @since 3.2.0
396 * @return boolean
397 */
398 public static function is_new_install() {
399 $product_count = array_sum( (array) wp_count_posts( 'product' ) );
400
401 return is_null( get_option( 'woocommerce_version', null ) ) || ( 0 === $product_count && -1 === wc_get_page_id( 'shop' ) );
402 }
403
404 /**
405 * Is a DB update needed?
406 *
407 * @since 3.2.0
408 * @return boolean
409 */
410 public static function needs_db_update() {
411 $current_db_version = get_option( 'woocommerce_db_version', null );
412 $updates = self::get_db_update_callbacks();
413 $update_versions = array_keys( $updates );
414 usort( $update_versions, 'version_compare' );
415
416 return ! is_null( $current_db_version ) && version_compare( $current_db_version, end( $update_versions ), '<' );
417 }
418
419 /**
420 * See if we need to set redirect transients for activation or not.
421 *
422 * @since 4.6.0
423 */
424 private static function maybe_set_activation_transients() {
425 if ( self::is_new_install() ) {
426 set_transient( '_wc_activation_redirect', 1, 30 );
427 }
428 }
429
430 /**
431 * See if we need to show or run database updates during install.
432 *
433 * @since 3.2.0
434 */
435 private static function maybe_update_db_version() {
436 if ( self::needs_db_update() ) {
437 if ( apply_filters( 'woocommerce_enable_auto_update_db', false ) ) {
438 self::update();
439 } else {
440 WC_Admin_Notices::add_notice( 'update', true );
441 }
442 } else {
443 self::update_db_version();
444 }
445 }
446
447 /**
448 * Update WC version to current.
449 */
450 private static function update_wc_version() {
451 update_option( 'woocommerce_version', WC()->version );
452 }
453
454 /**
455 * Get list of DB update callbacks.
456 *
457 * @since 3.0.0
458 * @return array
459 */
460 public static function get_db_update_callbacks() {
461 return self::$db_updates;
462 }
463
464 /**
465 * Push all needed DB updates to the queue for processing.
466 */
467 private static function update() {
468 $current_db_version = get_option( 'woocommerce_db_version' );
469 $loop = 0;
470
471 foreach ( self::get_db_update_callbacks() as $version => $update_callbacks ) {
472 if ( version_compare( $current_db_version, $version, '<' ) ) {
473 foreach ( $update_callbacks as $update_callback ) {
474 WC()->queue()->schedule_single(
475 time() + $loop,
476 'woocommerce_run_update_callback',
477 array(
478 'update_callback' => $update_callback,
479 ),
480 'woocommerce-db-updates'
481 );
482 $loop++;
483 }
484 }
485 }
486 }
487
488 /**
489 * Update DB version to current.
490 *
491 * @param string|null $version New WooCommerce DB version or null.
492 */
493 public static function update_db_version( $version = null ) {
494 update_option( 'woocommerce_db_version', is_null( $version ) ? WC()->version : $version );
495 }
496
497 /**
498 * Add more cron schedules.
499 *
500 * @param array $schedules List of WP scheduled cron jobs.
501 *
502 * @return array
503 */
504 public static function cron_schedules( $schedules ) {
505 $schedules['monthly'] = array(
506 'interval' => 2635200,
507 'display' => __( 'Monthly', 'woocommerce' ),
508 );
509 $schedules['fifteendays'] = array(
510 'interval' => 1296000,
511 'display' => __( 'Every 15 Days', 'woocommerce' ),
512 );
513 return $schedules;
514 }
515
516 /**
517 * Create cron jobs (clear them first).
518 */
519 private static function create_cron_jobs() {
520 wp_clear_scheduled_hook( 'woocommerce_scheduled_sales' );
521 wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
522 wp_clear_scheduled_hook( 'woocommerce_cleanup_sessions' );
523 wp_clear_scheduled_hook( 'woocommerce_cleanup_personal_data' );
524 wp_clear_scheduled_hook( 'woocommerce_cleanup_logs' );
525 wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
526 wp_clear_scheduled_hook( 'woocommerce_tracker_send_event' );
527
528 $ve = get_option( 'gmt_offset' ) > 0 ? '-' : '+';
529
530 wp_schedule_event( strtotime( '00:00 tomorrow ' . $ve . absint( get_option( 'gmt_offset' ) ) . ' HOURS' ), 'daily', 'woocommerce_scheduled_sales' );
531
532 $held_duration = get_option( 'woocommerce_hold_stock_minutes', '60' );
533
534 if ( '' !== $held_duration ) {
535 $cancel_unpaid_interval = apply_filters( 'woocommerce_cancel_unpaid_orders_interval_minutes', absint( $held_duration ) );
536 wp_schedule_single_event( time() + ( absint( $cancel_unpaid_interval ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
537 }
538
539 // Delay the first run of `woocommerce_cleanup_personal_data` by 10 seconds
540 // so it doesn't occur in the same request. WooCommerce Admin also schedules
541 // a daily cron that gets lost due to a race condition. WC_Privacy's background
542 // processing instance updates the cron schedule from within a cron job.
543 wp_schedule_event( time() + 10, 'daily', 'woocommerce_cleanup_personal_data' );
544 wp_schedule_event( time() + ( 3 * HOUR_IN_SECONDS ), 'daily', 'woocommerce_cleanup_logs' );
545 wp_schedule_event( time() + ( 6 * HOUR_IN_SECONDS ), 'twicedaily', 'woocommerce_cleanup_sessions' );
546 wp_schedule_event( time() + MINUTE_IN_SECONDS, 'fifteendays', 'woocommerce_geoip_updater' );
547 wp_schedule_event( time() + 10, apply_filters( 'woocommerce_tracker_event_recurrence', 'daily' ), 'woocommerce_tracker_send_event' );
548 }
549
550 /**
551 * Create pages on installation.
552 */
553 public static function maybe_create_pages() {
554 if ( empty( get_option( 'woocommerce_db_version' ) ) ) {
555 self::create_pages();
556 }
557 }
558
559 /**
560 * Create pages that the plugin relies on, storing page IDs in variables.
561 */
562 public static function create_pages() {
563 include_once dirname( __FILE__ ) . '/admin/wc-admin-functions.php';
564
565 $pages = apply_filters(
566 'woocommerce_create_pages',
567 array(
568 'shop' => array(
569 'name' => _x( 'shop', 'Page slug', 'woocommerce' ),
570 'title' => _x( 'Shop', 'Page title', 'woocommerce' ),
571 'content' => '',
572 ),
573 'cart' => array(
574 'name' => _x( 'cart', 'Page slug', 'woocommerce' ),
575 'title' => _x( 'Cart', 'Page title', 'woocommerce' ),
576 'content' => '<!-- wp:shortcode -->[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']<!-- /wp:shortcode -->',
577 ),
578 'checkout' => array(
579 'name' => _x( 'checkout', 'Page slug', 'woocommerce' ),
580 'title' => _x( 'Checkout', 'Page title', 'woocommerce' ),
581 'content' => '<!-- wp:shortcode -->[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']<!-- /wp:shortcode -->',
582 ),
583 'myaccount' => array(
584 'name' => _x( 'my-account', 'Page slug', 'woocommerce' ),
585 'title' => _x( 'My account', 'Page title', 'woocommerce' ),
586 'content' => '<!-- wp:shortcode -->[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']<!-- /wp:shortcode -->',
587 ),
588 'refund_returns' => array(
589 'name' => _x( 'refund_returns', 'Page slug', 'woocommerce' ),
590 'title' => _x( 'Refund and Returns Policy', 'Page title', 'woocommerce' ),
591 'content' => self::get_refunds_return_policy_page_content(),
592 'post_status' => 'draft',
593 ),
594 )
595 );
596
597 foreach ( $pages as $key => $page ) {
598 wc_create_page(
599 esc_sql( $page['name'] ),
600 'woocommerce_' . $key . '_page_id',
601 $page['title'],
602 $page['content'],
603 ! empty( $page['parent'] ) ? wc_get_page_id( $page['parent'] ) : '',
604 ! empty( $page['post_status'] ) ? $page['post_status'] : 'publish'
605 );
606 }
607 }
608
609 /**
610 * Default options.
611 *
612 * Sets up the default options used on the settings page.
613 */
614 private static function create_options() {
615 // Include settings so that we can run through defaults.
616 include_once dirname( __FILE__ ) . '/admin/class-wc-admin-settings.php';
617
618 $settings = WC_Admin_Settings::get_settings_pages();
619
620 foreach ( $settings as $section ) {
621 if ( ! method_exists( $section, 'get_settings' ) ) {
622 continue;
623 }
624 $subsections = array_unique( array_merge( array( '' ), array_keys( $section->get_sections() ) ) );
625
626 /**
627 * We are using 'WC_Settings_Page::get_settings' on purpose even thought it's deprecated.
628 * See the method documentation for an explanation.
629 */
630
631 foreach ( $subsections as $subsection ) {
632 foreach ( $section->get_settings( $subsection ) as $value ) {
633 if ( isset( $value['default'] ) && isset( $value['id'] ) ) {
634 $autoload = isset( $value['autoload'] ) ? (bool) $value['autoload'] : true;
635 add_option( $value['id'], $value['default'], '', ( $autoload ? 'yes' : 'no' ) );
636 }
637 }
638 }
639 }
640
641 // Define other defaults if not in setting screens.
642 add_option( 'woocommerce_single_image_width', '600', '', 'yes' );
643 add_option( 'woocommerce_thumbnail_image_width', '300', '', 'yes' );
644 add_option( 'woocommerce_checkout_highlight_required_fields', 'yes', '', 'yes' );
645 add_option( 'woocommerce_demo_store', 'no', '', 'no' );
646
647 if ( self::is_new_install() ) {
648 // Define initial tax classes.
649 WC_Tax::create_tax_class( __( 'Reduced rate', 'woocommerce' ) );
650 WC_Tax::create_tax_class( __( 'Zero rate', 'woocommerce' ) );
651 }
652 }
653
654 /**
655 * Add the default terms for WC taxonomies - product types and order statuses. Modify this at your own risk.
656 */
657 public static function create_terms() {
658 $taxonomies = array(
659 'product_type' => array(
660 'simple',
661 'grouped',
662 'variable',
663 'external',
664 ),
665 'product_visibility' => array(
666 'exclude-from-search',
667 'exclude-from-catalog',
668 'featured',
669 'outofstock',
670 'rated-1',
671 'rated-2',
672 'rated-3',
673 'rated-4',
674 'rated-5',
675 ),
676 );
677
678 foreach ( $taxonomies as $taxonomy => $terms ) {
679 foreach ( $terms as $term ) {
680 if ( ! get_term_by( 'name', $term, $taxonomy ) ) { // @codingStandardsIgnoreLine.
681 wp_insert_term( $term, $taxonomy );
682 }
683 }
684 }
685
686 $woocommerce_default_category = (int) get_option( 'default_product_cat', 0 );
687
688 if ( ! $woocommerce_default_category || ! term_exists( $woocommerce_default_category, 'product_cat' ) ) {
689 $default_product_cat_id = 0;
690 $default_product_cat_slug = sanitize_title( _x( 'Uncategorized', 'Default category slug', 'woocommerce' ) );
691 $default_product_cat = get_term_by( 'slug', $default_product_cat_slug, 'product_cat' ); // @codingStandardsIgnoreLine.
692
693 if ( $default_product_cat ) {
694 $default_product_cat_id = absint( $default_product_cat->term_taxonomy_id );
695 } else {
696 $result = wp_insert_term( _x( 'Uncategorized', 'Default category slug', 'woocommerce' ), 'product_cat', array( 'slug' => $default_product_cat_slug ) );
697
698 if ( ! is_wp_error( $result ) && ! empty( $result['term_taxonomy_id'] ) ) {
699 $default_product_cat_id = absint( $result['term_taxonomy_id'] );
700 }
701 }
702
703 if ( $default_product_cat_id ) {
704 update_option( 'default_product_cat', $default_product_cat_id );
705 }
706 }
707 }
708
709 /**
710 * Set up the database tables which the plugin needs to function.
711 * WARNING: If you are modifying this method, make sure that its safe to call regardless of the state of database.
712 *
713 * 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`.
714 *
715 * TODO: Add all crucial tables that we have created from workers in the past.
716 *
717 * Tables:
718 * woocommerce_attribute_taxonomies - Table for storing attribute taxonomies - these are user defined
719 * woocommerce_downloadable_product_permissions - Table for storing user and guest download permissions.
720 * KEY(order_id, product_id, download_id) used for organizing downloads on the My Account page
721 * woocommerce_order_items - Order line items are stored in a table to make them easily queryable for reports
722 * woocommerce_order_itemmeta - Order line item meta is stored in a table for storing extra data.
723 * woocommerce_tax_rates - Tax Rates are stored inside 2 tables making tax queries simple and efficient.
724 * woocommerce_tax_rate_locations - Each rate can be applied to more than one postcode/city hence the second table.
725 */
726 private static function create_tables() {
727 global $wpdb;
728
729 $wpdb->hide_errors();
730
731 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
732
733 /**
734 * Before updating with DBDELTA, remove any primary keys which could be
735 * modified due to schema updates.
736 */
737 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_downloadable_product_permissions';" ) ) {
738 if ( ! $wpdb->get_var( "SHOW COLUMNS FROM `{$wpdb->prefix}woocommerce_downloadable_product_permissions` LIKE 'permission_id';" ) ) {
739 $wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions DROP PRIMARY KEY, ADD `permission_id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;" );
740 }
741 }
742
743 /**
744 * Change wp_woocommerce_sessions schema to use a bigint auto increment field instead of char(32) field as
745 * 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
746 * there were reports of issues with this table (see https://github.com/woocommerce/woocommerce/issues/20912).
747 *
748 * This query needs to run before dbDelta() as this WP function is not able to handle primary key changes
749 * (see https://github.com/woocommerce/woocommerce/issues/21534 and https://core.trac.wordpress.org/ticket/40357).
750 */
751 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_sessions'" ) ) {
752 if ( ! $wpdb->get_var( "SHOW KEYS FROM {$wpdb->prefix}woocommerce_sessions WHERE Key_name = 'PRIMARY' AND Column_name = 'session_id'" ) ) {
753 $wpdb->query(
754 "ALTER TABLE `{$wpdb->prefix}woocommerce_sessions` DROP PRIMARY KEY, DROP KEY `session_id`, ADD PRIMARY KEY(`session_id`), ADD UNIQUE KEY(`session_key`)"
755 );
756 }
757 }
758
759 dbDelta( self::get_schema() );
760
761 $index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE column_name = 'comment_type' and key_name = 'woo_idx_comment_type'" );
762
763 if ( is_null( $index_exists ) ) {
764 // Add an index to the field comment_type to improve the response time of the query
765 // used by WC_Comments::wp_count_comments() to get the number of comments by type.
766 $wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_type (comment_type)" );
767 }
768
769 // Get tables data types and check it matches before adding constraint.
770 $download_log_columns = $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}wc_download_log WHERE Field = 'permission_id'", ARRAY_A );
771 $download_log_column_type = '';
772 if ( isset( $download_log_columns[0]['Type'] ) ) {
773 $download_log_column_type = $download_log_columns[0]['Type'];
774 }
775
776 $download_permissions_columns = $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE Field = 'permission_id'", ARRAY_A );
777 $download_permissions_column_type = '';
778 if ( isset( $download_permissions_columns[0]['Type'] ) ) {
779 $download_permissions_column_type = $download_permissions_columns[0]['Type'];
780 }
781
782 // Add constraint to download logs if the columns matches.
783 if ( ! empty( $download_permissions_column_type ) && ! empty( $download_log_column_type ) && $download_permissions_column_type === $download_log_column_type ) {
784 $fk_result = $wpdb->get_row( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log" );
785 if ( false === strpos( $fk_result->{'Create Table'}, "fk_{$wpdb->prefix}wc_download_log_permission_id" ) ) {
786 $wpdb->query(
787 "ALTER TABLE `{$wpdb->prefix}wc_download_log`
788 ADD CONSTRAINT `fk_{$wpdb->prefix}wc_download_log_permission_id`
789 FOREIGN KEY (`permission_id`)
790 REFERENCES `{$wpdb->prefix}woocommerce_downloadable_product_permissions` (`permission_id`) ON DELETE CASCADE;"
791 );
792 }
793 }
794
795 // Clear table caches.
796 delete_transient( 'wc_attribute_taxonomies' );
797 }
798
799 /**
800 * Get Table schema.
801 *
802 * See https://github.com/woocommerce/woocommerce/wiki/Database-Description/
803 *
804 * A note on indexes; Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
805 * As of WordPress 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which
806 * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
807 *
808 * Changing indexes may cause duplicate index notices in logs due to https://core.trac.wordpress.org/ticket/34870 but dropping
809 * indexes first causes too much load on some servers/larger DB.
810 *
811 * When adding or removing a table, make sure to update the list of tables in WC_Install::get_tables().
812 *
813 * @return string
814 */
815 private static function get_schema() {
816 global $wpdb;
817
818 $collate = '';
819
820 if ( $wpdb->has_cap( 'collation' ) ) {
821 $collate = $wpdb->get_charset_collate();
822 }
823
824 /*
825 * Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
826 * As of WP 4.2, however, they moved to utf8mb4, which uses 4 bytes per character. This means that an index which
827 * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
828 */
829 $max_index_length = 191;
830
831 $tables = "
832 CREATE TABLE {$wpdb->prefix}woocommerce_sessions (
833 session_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
834 session_key char(32) NOT NULL,
835 session_value longtext NOT NULL,
836 session_expiry BIGINT UNSIGNED NOT NULL,
837 PRIMARY KEY (session_id),
838 UNIQUE KEY session_key (session_key)
839 ) $collate;
840 CREATE TABLE {$wpdb->prefix}woocommerce_api_keys (
841 key_id BIGINT UNSIGNED NOT NULL auto_increment,
842 user_id BIGINT UNSIGNED NOT NULL,
843 description varchar(200) NULL,
844 permissions varchar(10) NOT NULL,
845 consumer_key char(64) NOT NULL,
846 consumer_secret char(43) NOT NULL,
847 nonces longtext NULL,
848 truncated_key char(7) NOT NULL,
849 last_access datetime NULL default null,
850 PRIMARY KEY (key_id),
851 KEY consumer_key (consumer_key),
852 KEY consumer_secret (consumer_secret)
853 ) $collate;
854 CREATE TABLE {$wpdb->prefix}woocommerce_attribute_taxonomies (
855 attribute_id BIGINT UNSIGNED NOT NULL auto_increment,
856 attribute_name varchar(200) NOT NULL,
857 attribute_label varchar(200) NULL,
858 attribute_type varchar(20) NOT NULL,
859 attribute_orderby varchar(20) NOT NULL,
860 attribute_public int(1) NOT NULL DEFAULT 1,
861 PRIMARY KEY (attribute_id),
862 KEY attribute_name (attribute_name(20))
863 ) $collate;
864 CREATE TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions (
865 permission_id BIGINT UNSIGNED NOT NULL auto_increment,
866 download_id varchar(36) NOT NULL,
867 product_id BIGINT UNSIGNED NOT NULL,
868 order_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
869 order_key varchar(200) NOT NULL,
870 user_email varchar(200) NOT NULL,
871 user_id BIGINT UNSIGNED NULL,
872 downloads_remaining varchar(9) NULL,
873 access_granted datetime NOT NULL default '0000-00-00 00:00:00',
874 access_expires datetime NULL default null,
875 download_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
876 PRIMARY KEY (permission_id),
877 KEY download_order_key_product (product_id,order_id,order_key(16),download_id),
878 KEY download_order_product (download_id,order_id,product_id),
879 KEY order_id (order_id),
880 KEY user_order_remaining_expires (user_id,order_id,downloads_remaining,access_expires)
881 ) $collate;
882 CREATE TABLE {$wpdb->prefix}woocommerce_order_items (
883 order_item_id BIGINT UNSIGNED NOT NULL auto_increment,
884 order_item_name TEXT NOT NULL,
885 order_item_type varchar(200) NOT NULL DEFAULT '',
886 order_id BIGINT UNSIGNED NOT NULL,
887 PRIMARY KEY (order_item_id),
888 KEY order_id (order_id)
889 ) $collate;
890 CREATE TABLE {$wpdb->prefix}woocommerce_order_itemmeta (
891 meta_id BIGINT UNSIGNED NOT NULL auto_increment,
892 order_item_id BIGINT UNSIGNED NOT NULL,
893 meta_key varchar(255) default NULL,
894 meta_value longtext NULL,
895 PRIMARY KEY (meta_id),
896 KEY order_item_id (order_item_id),
897 KEY meta_key (meta_key(32))
898 ) $collate;
899 CREATE TABLE {$wpdb->prefix}woocommerce_tax_rates (
900 tax_rate_id BIGINT UNSIGNED NOT NULL auto_increment,
901 tax_rate_country varchar(2) NOT NULL DEFAULT '',
902 tax_rate_state varchar(200) NOT NULL DEFAULT '',
903 tax_rate varchar(8) NOT NULL DEFAULT '',
904 tax_rate_name varchar(200) NOT NULL DEFAULT '',
905 tax_rate_priority BIGINT UNSIGNED NOT NULL,
906 tax_rate_compound int(1) NOT NULL DEFAULT 0,
907 tax_rate_shipping int(1) NOT NULL DEFAULT 1,
908 tax_rate_order BIGINT UNSIGNED NOT NULL,
909 tax_rate_class varchar(200) NOT NULL DEFAULT '',
910 PRIMARY KEY (tax_rate_id),
911 KEY tax_rate_country (tax_rate_country),
912 KEY tax_rate_state (tax_rate_state(2)),
913 KEY tax_rate_class (tax_rate_class(10)),
914 KEY tax_rate_priority (tax_rate_priority)
915 ) $collate;
916 CREATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations (
917 location_id BIGINT UNSIGNED NOT NULL auto_increment,
918 location_code varchar(200) NOT NULL,
919 tax_rate_id BIGINT UNSIGNED NOT NULL,
920 location_type varchar(40) NOT NULL,
921 PRIMARY KEY (location_id),
922 KEY tax_rate_id (tax_rate_id),
923 KEY location_type_code (location_type(10),location_code(20))
924 ) $collate;
925 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zones (
926 zone_id BIGINT UNSIGNED NOT NULL auto_increment,
927 zone_name varchar(200) NOT NULL,
928 zone_order BIGINT UNSIGNED NOT NULL,
929 PRIMARY KEY (zone_id)
930 ) $collate;
931 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_locations (
932 location_id BIGINT UNSIGNED NOT NULL auto_increment,
933 zone_id BIGINT UNSIGNED NOT NULL,
934 location_code varchar(200) NOT NULL,
935 location_type varchar(40) NOT NULL,
936 PRIMARY KEY (location_id),
937 KEY location_id (location_id),
938 KEY location_type_code (location_type(10),location_code(20))
939 ) $collate;
940 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_methods (
941 zone_id BIGINT UNSIGNED NOT NULL,
942 instance_id BIGINT UNSIGNED NOT NULL auto_increment,
943 method_id varchar(200) NOT NULL,
944 method_order BIGINT UNSIGNED NOT NULL,
945 is_enabled tinyint(1) NOT NULL DEFAULT '1',
946 PRIMARY KEY (instance_id)
947 ) $collate;
948 CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokens (
949 token_id BIGINT UNSIGNED NOT NULL auto_increment,
950 gateway_id varchar(200) NOT NULL,
951 token text NOT NULL,
952 user_id BIGINT UNSIGNED NOT NULL DEFAULT '0',
953 type varchar(200) NOT NULL,
954 is_default tinyint(1) NOT NULL DEFAULT '0',
955 PRIMARY KEY (token_id),
956 KEY user_id (user_id)
957 ) $collate;
958 CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokenmeta (
959 meta_id BIGINT UNSIGNED NOT NULL auto_increment,
960 payment_token_id BIGINT UNSIGNED NOT NULL,
961 meta_key varchar(255) NULL,
962 meta_value longtext NULL,
963 PRIMARY KEY (meta_id),
964 KEY payment_token_id (payment_token_id),
965 KEY meta_key (meta_key(32))
966 ) $collate;
967 CREATE TABLE {$wpdb->prefix}woocommerce_log (
968 log_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
969 timestamp datetime NOT NULL,
970 level smallint(4) NOT NULL,
971 source varchar(200) NOT NULL,
972 message longtext NOT NULL,
973 context longtext NULL,
974 PRIMARY KEY (log_id),
975 KEY level (level)
976 ) $collate;
977 CREATE TABLE {$wpdb->prefix}wc_webhooks (
978 webhook_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
979 status varchar(200) NOT NULL,
980 name text NOT NULL,
981 user_id BIGINT UNSIGNED NOT NULL,
982 delivery_url text NOT NULL,
983 secret text NOT NULL,
984 topic varchar(200) NOT NULL,
985 date_created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
986 date_created_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
987 date_modified datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
988 date_modified_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
989 api_version smallint(4) NOT NULL,
990 failure_count smallint(10) NOT NULL DEFAULT '0',
991 pending_delivery tinyint(1) NOT NULL DEFAULT '0',
992 PRIMARY KEY (webhook_id),
993 KEY user_id (user_id)
994 ) $collate;
995 CREATE TABLE {$wpdb->prefix}wc_download_log (
996 download_log_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
997 timestamp datetime NOT NULL,
998 permission_id BIGINT UNSIGNED NOT NULL,
999 user_id BIGINT UNSIGNED NULL,
1000 user_ip_address VARCHAR(100) NULL DEFAULT '',
1001 PRIMARY KEY (download_log_id),
1002 KEY permission_id (permission_id),
1003 KEY timestamp (timestamp)
1004 ) $collate;
1005 CREATE TABLE {$wpdb->prefix}wc_product_meta_lookup (
1006 `product_id` bigint(20) NOT NULL,
1007 `sku` varchar(100) NULL default '',
1008 `virtual` tinyint(1) NULL default 0,
1009 `downloadable` tinyint(1) NULL default 0,
1010 `min_price` decimal(19,4) NULL default NULL,
1011 `max_price` decimal(19,4) NULL default NULL,
1012 `onsale` tinyint(1) NULL default 0,
1013 `stock_quantity` double NULL default NULL,
1014 `stock_status` varchar(100) NULL default 'instock',
1015 `rating_count` bigint(20) NULL default 0,
1016 `average_rating` decimal(3,2) NULL default 0.00,
1017 `total_sales` bigint(20) NULL default 0,
1018 `tax_status` varchar(100) NULL default 'taxable',
1019 `tax_class` varchar(100) NULL default '',
1020 PRIMARY KEY (`product_id`),
1021 KEY `virtual` (`virtual`),
1022 KEY `downloadable` (`downloadable`),
1023 KEY `stock_status` (`stock_status`),
1024 KEY `stock_quantity` (`stock_quantity`),
1025 KEY `onsale` (`onsale`),
1026 KEY min_max_price (`min_price`, `max_price`)
1027 ) $collate;
1028 CREATE TABLE {$wpdb->prefix}wc_tax_rate_classes (
1029 tax_rate_class_id BIGINT UNSIGNED NOT NULL auto_increment,
1030 name varchar(200) NOT NULL DEFAULT '',
1031 slug varchar(200) NOT NULL DEFAULT '',
1032 PRIMARY KEY (tax_rate_class_id),
1033 UNIQUE KEY slug (slug($max_index_length))
1034 ) $collate;
1035 CREATE TABLE {$wpdb->prefix}wc_reserved_stock (
1036 `order_id` bigint(20) NOT NULL,
1037 `product_id` bigint(20) NOT NULL,
1038 `stock_quantity` double NOT NULL DEFAULT 0,
1039 `timestamp` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1040 `expires` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1041 PRIMARY KEY (`order_id`, `product_id`)
1042 ) $collate;
1043 ";
1044
1045 return $tables;
1046 }
1047
1048 /**
1049 * Return a list of WooCommerce tables. Used to make sure all WC tables are dropped when uninstalling the plugin
1050 * in a single site or multi site environment.
1051 *
1052 * @return array WC tables.
1053 */
1054 public static function get_tables() {
1055 global $wpdb;
1056
1057 $tables = array(
1058 "{$wpdb->prefix}wc_download_log",
1059 "{$wpdb->prefix}wc_product_meta_lookup",
1060 "{$wpdb->prefix}wc_tax_rate_classes",
1061 "{$wpdb->prefix}wc_webhooks",
1062 "{$wpdb->prefix}woocommerce_api_keys",
1063 "{$wpdb->prefix}woocommerce_attribute_taxonomies",
1064 "{$wpdb->prefix}woocommerce_downloadable_product_permissions",
1065 "{$wpdb->prefix}woocommerce_log",
1066 "{$wpdb->prefix}woocommerce_order_itemmeta",
1067 "{$wpdb->prefix}woocommerce_order_items",
1068 "{$wpdb->prefix}woocommerce_payment_tokenmeta",
1069 "{$wpdb->prefix}woocommerce_payment_tokens",
1070 "{$wpdb->prefix}woocommerce_sessions",
1071 "{$wpdb->prefix}woocommerce_shipping_zone_locations",
1072 "{$wpdb->prefix}woocommerce_shipping_zone_methods",
1073 "{$wpdb->prefix}woocommerce_shipping_zones",
1074 "{$wpdb->prefix}woocommerce_tax_rate_locations",
1075 "{$wpdb->prefix}woocommerce_tax_rates",
1076 "{$wpdb->prefix}wc_reserved_stock",
1077 );
1078
1079 /**
1080 * Filter the list of known WooCommerce tables.
1081 *
1082 * If WooCommerce plugins need to add new tables, they can inject them here.
1083 *
1084 * @param array $tables An array of WooCommerce-specific database table names.
1085 */
1086 $tables = apply_filters( 'woocommerce_install_get_tables', $tables );
1087
1088 return $tables;
1089 }
1090
1091 /**
1092 * Drop WooCommerce tables.
1093 *
1094 * @return void
1095 */
1096 public static function drop_tables() {
1097 global $wpdb;
1098
1099 $tables = self::get_tables();
1100
1101 foreach ( $tables as $table ) {
1102 $wpdb->query( "DROP TABLE IF EXISTS {$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1103 }
1104 }
1105
1106 /**
1107 * Uninstall tables when MU blog is deleted.
1108 *
1109 * @param array $tables List of tables that will be deleted by WP.
1110 *
1111 * @return string[]
1112 */
1113 public static function wpmu_drop_tables( $tables ) {
1114 return array_merge( $tables, self::get_tables() );
1115 }
1116
1117 /**
1118 * Create roles and capabilities.
1119 */
1120 public static function create_roles() {
1121 global $wp_roles;
1122
1123 if ( ! class_exists( 'WP_Roles' ) ) {
1124 return;
1125 }
1126
1127 if ( ! isset( $wp_roles ) ) {
1128 $wp_roles = new WP_Roles(); // @codingStandardsIgnoreLine
1129 }
1130
1131 // Dummy gettext calls to get strings in the catalog.
1132 /* translators: user role */
1133 _x( 'Customer', 'User role', 'woocommerce' );
1134 /* translators: user role */
1135 _x( 'Shop manager', 'User role', 'woocommerce' );
1136
1137 // Customer role.
1138 add_role(
1139 'customer',
1140 'Customer',
1141 array(
1142 'read' => true,
1143 )
1144 );
1145
1146 // Shop manager role.
1147 add_role(
1148 'shop_manager',
1149 'Shop manager',
1150 array(
1151 'level_9' => true,
1152 'level_8' => true,
1153 'level_7' => true,
1154 'level_6' => true,
1155 'level_5' => true,
1156 'level_4' => true,
1157 'level_3' => true,
1158 'level_2' => true,
1159 'level_1' => true,
1160 'level_0' => true,
1161 'read' => true,
1162 'read_private_pages' => true,
1163 'read_private_posts' => true,
1164 'edit_posts' => true,
1165 'edit_pages' => true,
1166 'edit_published_posts' => true,
1167 'edit_published_pages' => true,
1168 'edit_private_pages' => true,
1169 'edit_private_posts' => true,
1170 'edit_others_posts' => true,
1171 'edit_others_pages' => true,
1172 'publish_posts' => true,
1173 'publish_pages' => true,
1174 'delete_posts' => true,
1175 'delete_pages' => true,
1176 'delete_private_pages' => true,
1177 'delete_private_posts' => true,
1178 'delete_published_pages' => true,
1179 'delete_published_posts' => true,
1180 'delete_others_posts' => true,
1181 'delete_others_pages' => true,
1182 'manage_categories' => true,
1183 'manage_links' => true,
1184 'moderate_comments' => true,
1185 'upload_files' => true,
1186 'export' => true,
1187 'import' => true,
1188 'list_users' => true,
1189 'edit_theme_options' => true,
1190 )
1191 );
1192
1193 $capabilities = self::get_core_capabilities();
1194
1195 foreach ( $capabilities as $cap_group ) {
1196 foreach ( $cap_group as $cap ) {
1197 $wp_roles->add_cap( 'shop_manager', $cap );
1198 $wp_roles->add_cap( 'administrator', $cap );
1199 }
1200 }
1201 }
1202
1203 /**
1204 * Get capabilities for WooCommerce - these are assigned to admin/shop manager during installation or reset.
1205 *
1206 * @return array
1207 */
1208 public static function get_core_capabilities() {
1209 $capabilities = array();
1210
1211 $capabilities['core'] = array(
1212 'manage_woocommerce',
1213 'view_woocommerce_reports',
1214 );
1215
1216 $capability_types = array( 'product', 'shop_order', 'shop_coupon' );
1217
1218 foreach ( $capability_types as $capability_type ) {
1219
1220 $capabilities[ $capability_type ] = array(
1221 // Post type.
1222 "edit_{$capability_type}",
1223 "read_{$capability_type}",
1224 "delete_{$capability_type}",
1225 "edit_{$capability_type}s",
1226 "edit_others_{$capability_type}s",
1227 "publish_{$capability_type}s",
1228 "read_private_{$capability_type}s",
1229 "delete_{$capability_type}s",
1230 "delete_private_{$capability_type}s",
1231 "delete_published_{$capability_type}s",
1232 "delete_others_{$capability_type}s",
1233 "edit_private_{$capability_type}s",
1234 "edit_published_{$capability_type}s",
1235
1236 // Terms.
1237 "manage_{$capability_type}_terms",
1238 "edit_{$capability_type}_terms",
1239 "delete_{$capability_type}_terms",
1240 "assign_{$capability_type}_terms",
1241 );
1242 }
1243
1244 return $capabilities;
1245 }
1246
1247 /**
1248 * Remove WooCommerce roles.
1249 */
1250 public static function remove_roles() {
1251 global $wp_roles;
1252
1253 if ( ! class_exists( 'WP_Roles' ) ) {
1254 return;
1255 }
1256
1257 if ( ! isset( $wp_roles ) ) {
1258 $wp_roles = new WP_Roles(); // @codingStandardsIgnoreLine
1259 }
1260
1261 $capabilities = self::get_core_capabilities();
1262
1263 foreach ( $capabilities as $cap_group ) {
1264 foreach ( $cap_group as $cap ) {
1265 $wp_roles->remove_cap( 'shop_manager', $cap );
1266 $wp_roles->remove_cap( 'administrator', $cap );
1267 }
1268 }
1269
1270 remove_role( 'customer' );
1271 remove_role( 'shop_manager' );
1272 }
1273
1274 /**
1275 * Create files/directories.
1276 */
1277 private static function create_files() {
1278 // Bypass if filesystem is read-only and/or non-standard upload system is used.
1279 if ( apply_filters( 'woocommerce_install_skip_create_files', false ) ) {
1280 return;
1281 }
1282
1283 // Install files and folders for uploading files and prevent hotlinking.
1284 $upload_dir = wp_get_upload_dir();
1285 $download_method = get_option( 'woocommerce_file_download_method', 'force' );
1286
1287 $files = array(
1288 array(
1289 'base' => $upload_dir['basedir'] . '/woocommerce_uploads',
1290 'file' => 'index.html',
1291 'content' => '',
1292 ),
1293 array(
1294 'base' => WC_LOG_DIR,
1295 'file' => '.htaccess',
1296 'content' => 'deny from all',
1297 ),
1298 array(
1299 'base' => WC_LOG_DIR,
1300 'file' => 'index.html',
1301 'content' => '',
1302 ),
1303 array(
1304 'base' => $upload_dir['basedir'] . '/woocommerce_uploads',
1305 'file' => '.htaccess',
1306 'content' => 'redirect' === $download_method ? 'Options -Indexes' : 'deny from all',
1307 ),
1308 );
1309
1310 foreach ( $files as $file ) {
1311 if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
1312 $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'wb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen
1313 if ( $file_handle ) {
1314 fwrite( $file_handle, $file['content'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
1315 fclose( $file_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
1316 }
1317 }
1318 }
1319
1320 // Create attachment for placeholders.
1321 self::create_placeholder_image();
1322 }
1323
1324 /**
1325 * Create a placeholder image in the media library.
1326 *
1327 * @since 3.5.0
1328 */
1329 private static function create_placeholder_image() {
1330 $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 );
1331
1332 // Validate current setting if set. If set, return.
1333 if ( ! empty( $placeholder_image ) ) {
1334 if ( ! is_numeric( $placeholder_image ) ) {
1335 return;
1336 } elseif ( $placeholder_image && wp_attachment_is_image( $placeholder_image ) ) {
1337 return;
1338 }
1339 }
1340
1341 $upload_dir = wp_upload_dir();
1342 $source = WC()->plugin_path() . '/assets/images/placeholder-attachment.png';
1343 $filename = $upload_dir['basedir'] . '/woocommerce-placeholder.png';
1344
1345 if ( ! file_exists( $filename ) ) {
1346 copy( $source, $filename ); // @codingStandardsIgnoreLine.
1347 }
1348
1349 if ( ! file_exists( $filename ) ) {
1350 update_option( 'woocommerce_placeholder_image', 0 );
1351 return;
1352 }
1353
1354 $filetype = wp_check_filetype( basename( $filename ), null );
1355 $attachment = array(
1356 'guid' => $upload_dir['url'] . '/' . basename( $filename ),
1357 'post_mime_type' => $filetype['type'],
1358 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $filename ) ),
1359 'post_content' => '',
1360 'post_status' => 'inherit',
1361 );
1362
1363 $attach_id = wp_insert_attachment( $attachment, $filename );
1364 if ( is_wp_error( $attach_id ) ) {
1365 update_option( 'woocommerce_placeholder_image', 0 );
1366 return;
1367 }
1368
1369 update_option( 'woocommerce_placeholder_image', $attach_id );
1370
1371 // Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
1372 require_once ABSPATH . 'wp-admin/includes/image.php';
1373
1374 // Generate the metadata for the attachment, and update the database record.
1375 $attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
1376 wp_update_attachment_metadata( $attach_id, $attach_data );
1377 }
1378
1379 /**
1380 * Show action links on the plugin screen.
1381 *
1382 * @param mixed $links Plugin Action links.
1383 *
1384 * @return array
1385 */
1386 public static function plugin_action_links( $links ) {
1387 $action_links = array(
1388 'settings' => '<a href="' . admin_url( 'admin.php?page=wc-settings' ) . '" aria-label="' . esc_attr__( 'View WooCommerce settings', 'woocommerce' ) . '">' . esc_html__( 'Settings', 'woocommerce' ) . '</a>',
1389 );
1390
1391 return array_merge( $action_links, $links );
1392 }
1393
1394 /**
1395 * Show row meta on the plugin screen.
1396 *
1397 * @param mixed $links Plugin Row Meta.
1398 * @param mixed $file Plugin Base file.
1399 *
1400 * @return array
1401 */
1402 public static function plugin_row_meta( $links, $file ) {
1403 if ( WC_PLUGIN_BASENAME !== $file ) {
1404 return $links;
1405 }
1406
1407 $row_meta = array(
1408 'docs' => '<a href="' . esc_url( apply_filters( 'woocommerce_docs_url', 'https://docs.woocommerce.com/documentation/plugins/woocommerce/' ) ) . '" aria-label="' . esc_attr__( 'View WooCommerce documentation', 'woocommerce' ) . '">' . esc_html__( 'Docs', 'woocommerce' ) . '</a>',
1409 'apidocs' => '<a href="' . esc_url( apply_filters( 'woocommerce_apidocs_url', 'https://docs.woocommerce.com/wc-apidocs/' ) ) . '" aria-label="' . esc_attr__( 'View WooCommerce API docs', 'woocommerce' ) . '">' . esc_html__( 'API docs', 'woocommerce' ) . '</a>',
1410 'support' => '<a href="' . esc_url( apply_filters( 'woocommerce_community_support_url', 'https://wordpress.org/support/plugin/woocommerce/' ) ) . '" aria-label="' . esc_attr__( 'Visit community forums', 'woocommerce' ) . '">' . esc_html__( 'Community support', 'woocommerce' ) . '</a>',
1411 );
1412
1413 if ( WCConnectionHelper::is_connected() ) {
1414 $row_meta['premium_support'] = '<a href="' . esc_url( apply_filters( 'woocommerce_support_url', 'https://woocommerce.com/my-account/create-a-ticket/' ) ) . '" aria-label="' . esc_attr__( 'Visit premium customer support', 'woocommerce' ) . '">' . esc_html__( 'Premium support', 'woocommerce' ) . '</a>';
1415 }
1416
1417 return array_merge( $links, $row_meta );
1418 }
1419
1420 /**
1421 * Get slug from path and associate it with the path.
1422 *
1423 * @param array $plugins Associative array of plugin files to paths.
1424 * @param string $key Plugin relative path. Example: woocommerce/woocommerce.php.
1425 */
1426 private static function associate_plugin_file( $plugins, $key ) {
1427 $path = explode( '/', $key );
1428 $filename = end( $path );
1429 $plugins[ $filename ] = $key;
1430 return $plugins;
1431 }
1432
1433 /**
1434 * Install a plugin from .org in the background via a cron job (used by
1435 * installer - opt in).
1436 *
1437 * @param string $plugin_to_install_id Plugin ID.
1438 * @param array $plugin_to_install Plugin information.
1439 *
1440 * @throws Exception If unable to proceed with plugin installation.
1441 * @since 2.6.0
1442 */
1443 public static function background_installer( $plugin_to_install_id, $plugin_to_install ) {
1444 // Explicitly clear the event.
1445 $args = func_get_args();
1446
1447 if ( ! empty( $plugin_to_install['repo-slug'] ) ) {
1448 require_once ABSPATH . 'wp-admin/includes/file.php';
1449 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
1450 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1451 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1452
1453 WP_Filesystem();
1454
1455 $skin = new Automatic_Upgrader_Skin();
1456 $upgrader = new WP_Upgrader( $skin );
1457 $installed_plugins = array_reduce( array_keys( get_plugins() ), array( __CLASS__, 'associate_plugin_file' ) );
1458 if ( empty( $installed_plugins ) ) {
1459 $installed_plugins = array();
1460 }
1461 $plugin_slug = $plugin_to_install['repo-slug'];
1462 $plugin_file = isset( $plugin_to_install['file'] ) ? $plugin_to_install['file'] : $plugin_slug . '.php';
1463 $installed = false;
1464 $activate = false;
1465
1466 // See if the plugin is installed already.
1467 if ( isset( $installed_plugins[ $plugin_file ] ) ) {
1468 $installed = true;
1469 $activate = ! is_plugin_active( $installed_plugins[ $plugin_file ] );
1470 }
1471
1472 // Install this thing!
1473 if ( ! $installed ) {
1474 // Suppress feedback.
1475 ob_start();
1476
1477 try {
1478 $plugin_information = plugins_api(
1479 'plugin_information',
1480 array(
1481 'slug' => $plugin_slug,
1482 'fields' => array(
1483 'short_description' => false,
1484 'sections' => false,
1485 'requires' => false,
1486 'rating' => false,
1487 'ratings' => false,
1488 'downloaded' => false,
1489 'last_updated' => false,
1490 'added' => false,
1491 'tags' => false,
1492 'homepage' => false,
1493 'donate_link' => false,
1494 'author_profile' => false,
1495 'author' => false,
1496 ),
1497 )
1498 );
1499
1500 if ( is_wp_error( $plugin_information ) ) {
1501 throw new Exception( $plugin_information->get_error_message() );
1502 }
1503
1504 $package = $plugin_information->download_link;
1505 $download = $upgrader->download_package( $package );
1506
1507 if ( is_wp_error( $download ) ) {
1508 throw new Exception( $download->get_error_message() );
1509 }
1510
1511 $working_dir = $upgrader->unpack_package( $download, true );
1512
1513 if ( is_wp_error( $working_dir ) ) {
1514 throw new Exception( $working_dir->get_error_message() );
1515 }
1516
1517 $result = $upgrader->install_package(
1518 array(
1519 'source' => $working_dir,
1520 'destination' => WP_PLUGIN_DIR,
1521 'clear_destination' => false,
1522 'abort_if_destination_exists' => false,
1523 'clear_working' => true,
1524 'hook_extra' => array(
1525 'type' => 'plugin',
1526 'action' => 'install',
1527 ),
1528 )
1529 );
1530
1531 if ( is_wp_error( $result ) ) {
1532 throw new Exception( $result->get_error_message() );
1533 }
1534
1535 $activate = true;
1536
1537 } catch ( Exception $e ) {
1538 WC_Admin_Notices::add_custom_notice(
1539 $plugin_to_install_id . '_install_error',
1540 sprintf(
1541 // translators: 1: plugin name, 2: error message, 3: URL to install plugin manually.
1542 __( '%1$s could not be installed (%2$s). <a href="%3$s">Please install it manually by clicking here.</a>', 'woocommerce' ),
1543 $plugin_to_install['name'],
1544 $e->getMessage(),
1545 esc_url( admin_url( 'index.php?wc-install-plugin-redirect=' . $plugin_slug ) )
1546 )
1547 );
1548 }
1549
1550 // Discard feedback.
1551 ob_end_clean();
1552 }
1553
1554 wp_clean_plugins_cache();
1555
1556 // Activate this thing.
1557 if ( $activate ) {
1558 try {
1559 add_action( 'add_option_mailchimp_woocommerce_plugin_do_activation_redirect', array( __CLASS__, 'remove_mailchimps_redirect' ), 10, 2 );
1560 $result = activate_plugin( $installed ? $installed_plugins[ $plugin_file ] : $plugin_slug . '/' . $plugin_file );
1561
1562 if ( is_wp_error( $result ) ) {
1563 throw new Exception( $result->get_error_message() );
1564 }
1565 } catch ( Exception $e ) {
1566 WC_Admin_Notices::add_custom_notice(
1567 $plugin_to_install_id . '_install_error',
1568 sprintf(
1569 // translators: 1: plugin name, 2: URL to WP plugin page.
1570 __( '%1$s was installed but could not be activated. <a href="%2$s">Please activate it manually by clicking here.</a>', 'woocommerce' ),
1571 $plugin_to_install['name'],
1572 admin_url( 'plugins.php' )
1573 )
1574 );
1575 }
1576 }
1577 }
1578 }
1579
1580 /**
1581 * Removes redirect added during MailChimp plugin's activation.
1582 *
1583 * @param string $option Option name.
1584 * @param string $value Option value.
1585 */
1586 public static function remove_mailchimps_redirect( $option, $value ) {
1587 // Remove this action to prevent infinite looping.
1588 remove_action( 'add_option_mailchimp_woocommerce_plugin_do_activation_redirect', array( __CLASS__, 'remove_mailchimps_redirect' ) );
1589
1590 // Update redirect back to false.
1591 update_option( 'mailchimp_woocommerce_plugin_do_activation_redirect', false );
1592 }
1593
1594 /**
1595 * Install a theme from .org in the background via a cron job (used by installer - opt in).
1596 *
1597 * @param string $theme_slug Theme slug.
1598 *
1599 * @throws Exception If unable to proceed with theme installation.
1600 * @since 3.1.0
1601 */
1602 public static function theme_background_installer( $theme_slug ) {
1603 // Explicitly clear the event.
1604 $args = func_get_args();
1605
1606 if ( ! empty( $theme_slug ) ) {
1607 // Suppress feedback.
1608 ob_start();
1609
1610 try {
1611 $theme = wp_get_theme( $theme_slug );
1612
1613 if ( ! $theme->exists() ) {
1614 require_once ABSPATH . 'wp-admin/includes/file.php';
1615 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1616 include_once ABSPATH . 'wp-admin/includes/theme.php';
1617
1618 WP_Filesystem();
1619
1620 $skin = new Automatic_Upgrader_Skin();
1621 $upgrader = new Theme_Upgrader( $skin );
1622 $api = themes_api(
1623 'theme_information',
1624 array(
1625 'slug' => $theme_slug,
1626 'fields' => array( 'sections' => false ),
1627 )
1628 );
1629 $result = $upgrader->install( $api->download_link );
1630
1631 if ( is_wp_error( $result ) ) {
1632 throw new Exception( $result->get_error_message() );
1633 } elseif ( is_wp_error( $skin->result ) ) {
1634 throw new Exception( $skin->result->get_error_message() );
1635 } elseif ( is_null( $result ) ) {
1636 throw new Exception( 'Unable to connect to the filesystem. Please confirm your credentials.' );
1637 }
1638 }
1639
1640 switch_theme( $theme_slug );
1641 } catch ( Exception $e ) {
1642 WC_Admin_Notices::add_custom_notice(
1643 $theme_slug . '_install_error',
1644 sprintf(
1645 // translators: 1: theme slug, 2: error message, 3: URL to install theme manually.
1646 __( '%1$s could not be installed (%2$s). <a href="%3$s">Please install it manually by clicking here.</a>', 'woocommerce' ),
1647 $theme_slug,
1648 $e->getMessage(),
1649 esc_url( admin_url( 'update.php?action=install-theme&theme=' . $theme_slug . '&_wpnonce=' . wp_create_nonce( 'install-theme_' . $theme_slug ) ) )
1650 )
1651 );
1652 }
1653
1654 // Discard feedback.
1655 ob_end_clean();
1656 }
1657 }
1658
1659 /**
1660 * Sets whether PayPal Standard will be loaded on install.
1661 *
1662 * @since 5.5.0
1663 */
1664 private static function set_paypal_standard_load_eligibility() {
1665 // Initiating the payment gateways sets the flag.
1666 if ( class_exists( 'WC_Gateway_Paypal' ) ) {
1667 ( new WC_Gateway_Paypal() )->should_load();
1668 }
1669 }
1670
1671 /**
1672 * Gets the content of the sample refunds and return policy page.
1673 *
1674 * @since 5.6.0
1675 * @return HTML The content for the page
1676 */
1677 private static function get_refunds_return_policy_page_content() {
1678 return <<<EOT
1679 <!-- wp:paragraph -->
1680 <p><b>This is a sample page.</b></p>
1681 <!-- /wp:paragraph -->
1682
1683 <!-- wp:paragraph -->
1684 <h3>Overview</h3>
1685 <!-- /wp:paragraph -->
1686
1687 <!-- wp:paragraph -->
1688 <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>
1689 <!-- /wp:paragraph -->
1690
1691 <!-- wp:paragraph -->
1692 <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>
1693 <!-- /wp:paragraph -->
1694
1695 <!-- wp:paragraph -->
1696 <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>
1697 <!-- /wp:paragraph -->
1698
1699 <!-- wp:paragraph -->
1700 <p>Additional non-returnable items:</p>
1701 <!-- /wp:paragraph -->
1702
1703 <!-- wp:list -->
1704 <ul>
1705 <li>Gift cards</li>
1706 <li>Downloadable software products</li>
1707 <li>Some health and personal care items</li>
1708 </ul>
1709 <!-- /wp:list -->
1710
1711 <!-- wp:paragraph -->
1712 <p>To complete your return, we require a receipt or proof of purchase.</p>
1713 <!-- /wp:paragraph -->
1714
1715 <!-- wp:paragraph -->
1716 <p>Please do not send your purchase back to the manufacturer.</p>
1717 <!-- /wp:paragraph -->
1718
1719 <!-- wp:paragraph -->
1720 <p>There are certain situations where only partial refunds are granted:</p>
1721 <!-- /wp:paragraph -->
1722
1723 <!-- wp:list -->
1724 <ul>
1725 <li>Book with obvious signs of use</li>
1726 <li>CD, DVD, VHS tape, software, video game, cassette tape, or vinyl record that has been opened.</li>
1727 <li>Any item not in its original condition, is damaged or missing parts for reasons not due to our error.</li>
1728 <li>Any item that is returned more than 30 days after delivery</li>
1729 </ul>
1730 <!-- /wp:list -->
1731
1732 <!-- wp:paragraph -->
1733 <h2>Refunds</h2>
1734 <!-- /wp:paragraph -->
1735
1736 <!-- wp:paragraph -->
1737 <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>
1738 <!-- /wp:paragraph -->
1739
1740 <!-- wp:paragraph -->
1741 <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>
1742 <!-- /wp:paragraph -->
1743
1744 <!-- wp:paragraph -->
1745 <b>Late or missing refunds</b>
1746 <!-- /wp:paragraph -->
1747
1748 <!-- wp:paragraph -->
1749 <p>If you haven’t received a refund yet, first check your bank account again.</p>
1750 <!-- /wp:paragraph -->
1751
1752 <!-- wp:paragraph -->
1753 <p>Then contact your credit card company, it may take some time before your refund is officially posted.</p>
1754 <!-- /wp:paragraph -->
1755
1756 <!-- wp:paragraph -->
1757 <p>Next contact your bank. There is often some processing time before a refund is posted.</p>
1758 <!-- /wp:paragraph -->
1759
1760 <!-- wp:paragraph -->
1761 <p>If you’ve done all of this and you still have not received your refund yet, please contact us at {email address}.</p>
1762 <!-- /wp:paragraph -->
1763
1764 <!-- wp:paragraph -->
1765 <b>Sale items</b>
1766 <!-- /wp:paragraph -->
1767
1768 <!-- wp:paragraph -->
1769 <p>Only regular priced items may be refunded. Sale items cannot be refunded.</p>
1770 <!-- /wp:paragraph -->
1771
1772 <!-- wp:paragraph -->
1773 <h2>Exchanges</h2>
1774 <!-- /wp:paragraph -->
1775
1776 <!-- wp:paragraph -->
1777 <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>
1778 <!-- /wp:paragraph -->
1779
1780 <!-- wp:paragraph -->
1781 <h2>Gifts</h2>
1782 <!-- /wp:paragraph -->
1783
1784 <!-- wp:paragraph -->
1785 <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>
1786 <!-- /wp:paragraph -->
1787
1788 <!-- wp:paragraph -->
1789 <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>
1790 <!-- /wp:paragraph -->
1791
1792 <!-- wp:paragraph -->
1793 <h2>Shipping returns</h2>
1794 <!-- /wp:paragraph -->
1795
1796 <!-- wp:paragraph -->
1797 <p>To return your product, you should mail your product to: {physical address}.</p>
1798 <!-- /wp:paragraph -->
1799
1800 <!-- wp:paragraph -->
1801 <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>
1802 <!-- /wp:paragraph -->
1803
1804 <!-- wp:paragraph -->
1805 <p>Depending on where you live, the time it may take for your exchanged product to reach you may vary.</p>
1806 <!-- /wp:paragraph -->
1807
1808 <!-- wp:paragraph -->
1809 <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>
1810 <!-- /wp:paragraph -->
1811
1812 <!-- wp:paragraph -->
1813 <h2>Need help?</h2>
1814 <!-- /wp:paragraph -->
1815
1816 <!-- wp:paragraph -->
1817 <p>Contact us at {email} for questions related to refunds and returns.</p>
1818 <!-- /wp:paragraph -->
1819 EOT;
1820 }
1821
1822 /**
1823 * Adds an admin inbox note after a page has been created to notify
1824 * user. For example to take action to edit the page such as the
1825 * Refund and returns page.
1826 *
1827 * @since 5.6.0
1828 * @return void
1829 */
1830 public static function add_admin_note_after_page_created() {
1831 if ( ! WC()->is_wc_admin_active() ) {
1832 return;
1833 }
1834
1835 $page_id = get_option( 'woocommerce_refund_returns_page_created', null );
1836
1837 if ( null === $page_id ) {
1838 return;
1839 }
1840
1841 WC_Notes_Refund_Returns::possibly_add_note( $page_id );
1842 }
1843
1844 /**
1845 * When pages are created, we might want to take some action.
1846 * In this case we want to set an option when refund and returns
1847 * page is created.
1848 *
1849 * @since 5.6.0
1850 * @param int $page_id ID of the page.
1851 * @param array $page_data The data of the page created.
1852 * @return void
1853 */
1854 public static function page_created( $page_id, $page_data ) {
1855 if ( 'refund_returns' === $page_data['post_name'] ) {
1856 delete_option( 'woocommerce_refund_returns_page_created' );
1857 add_option( 'woocommerce_refund_returns_page_created', $page_id, '', false );
1858 }
1859 }
1860 }
1861
1862 WC_Install::init();
1863