PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 4.6.4
WooCommerce v4.6.4
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 5 years ago admin 5 years ago cli 5 years ago customizer 5 years ago data-stores 4 years ago emails 5 years ago export 5 years ago gateways 5 years ago import 5 years ago integrations 5 years ago interfaces 5 years ago legacy 5 years ago libraries 5 years ago log-handlers 5 years ago payment-tokens 5 years ago queue 5 years ago rest-api 5 years ago shipping 5 years ago shortcodes 5 years ago theme-support 5 years ago tracks 5 years ago traits 5 years ago walkers 5 years ago wccom-site 5 years ago widgets 5 years ago class-wc-ajax.php 5 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 5 years ago class-wc-cart-fees.php 5 years ago class-wc-cart-session.php 5 years ago class-wc-cart-totals.php 5 years ago class-wc-cart.php 5 years ago class-wc-checkout.php 5 years ago class-wc-cli.php 5 years ago class-wc-comments.php 5 years ago class-wc-countries.php 5 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 5 years ago class-wc-data-exception.php 5 years ago class-wc-data-store.php 5 years ago class-wc-datetime.php 5 years ago class-wc-deprecated-action-hooks.php 5 years ago class-wc-deprecated-filter-hooks.php 5 years ago class-wc-discounts.php 5 years ago class-wc-download-handler.php 5 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 5 years ago class-wc-geo-ip.php 5 years ago class-wc-geolite-integration.php 5 years ago class-wc-geolocation.php 5 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 5 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 5 years ago class-wc-order-refund.php 5 years ago class-wc-order.php 5 years ago class-wc-payment-gateways.php 5 years ago class-wc-payment-tokens.php 5 years ago class-wc-post-data.php 5 years ago class-wc-post-types.php 5 years ago class-wc-privacy-background-process.php 5 years ago class-wc-privacy-erasers.php 5 years ago class-wc-privacy-exporters.php 5 years ago class-wc-privacy.php 5 years ago class-wc-product-attribute.php 5 years ago class-wc-product-download.php 5 years ago class-wc-product-external.php 5 years ago class-wc-product-factory.php 5 years ago class-wc-product-grouped.php 5 years ago class-wc-product-query.php 5 years ago class-wc-product-simple.php 5 years ago class-wc-product-variable.php 5 years ago class-wc-product-variation.php 5 years ago class-wc-query.php 5 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 5 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 5 years ago class-wc-tracker.php 5 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 5 years ago wc-coupon-functions.php 5 years ago wc-deprecated-functions.php 5 years ago wc-formatting-functions.php 5 years ago wc-notice-functions.php 5 years ago wc-order-functions.php 5 years ago wc-order-item-functions.php 5 years ago wc-page-functions.php 5 years ago wc-product-functions.php 5 years ago wc-rest-functions.php 5 years ago wc-stock-functions.php 5 years ago wc-template-functions.php 5 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
1630 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 '4.6.4' => array(
161 'Automattic\\WooCommerce\\Admin\\ReportCSVExporter::maybe_create_directory',
162 'wc_admin_update_164_delete_report_downloads',
163 'wc_update_464_db_version',
164 ),
165 );
166
167 /**
168 * Hook in tabs.
169 */
170 public static function init() {
171 add_action( 'init', array( __CLASS__, 'check_version' ), 5 );
172 add_action( 'init', array( __CLASS__, 'manual_database_update' ), 20 );
173 add_action( 'admin_init', array( __CLASS__, 'wc_admin_db_update_notice' ) );
174 add_action( 'woocommerce_run_update_callback', array( __CLASS__, 'run_update_callback' ) );
175 add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
176 add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
177 add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
178 add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
179 add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
180 }
181
182 /**
183 * Check WooCommerce version and run the updater is required.
184 *
185 * This check is done on all requests and runs if the versions do not match.
186 */
187 public static function check_version() {
188 if ( ! Constants::is_defined( 'IFRAME_REQUEST' ) && version_compare( get_option( 'woocommerce_version' ), WC()->version, '<' ) ) {
189 self::install();
190 do_action( 'woocommerce_updated' );
191 }
192 }
193
194 /**
195 * Performan manual database update when triggered by WooCommerce System Tools.
196 *
197 * @since 3.6.5
198 */
199 public static function manual_database_update() {
200 $blog_id = get_current_blog_id();
201
202 add_action( 'wp_' . $blog_id . '_wc_updater_cron', array( __CLASS__, 'run_manual_database_update' ) );
203 }
204
205 /**
206 * Add WC Admin based db update notice.
207 *
208 * @since 4.0.0
209 */
210 public static function wc_admin_db_update_notice() {
211 if (
212 WC()->is_wc_admin_active() &&
213 false !== get_option( 'woocommerce_admin_install_timestamp' )
214 ) {
215 new WC_Notes_Run_Db_Update();
216 }
217 }
218
219 /**
220 * Run manual database update.
221 */
222 public static function run_manual_database_update() {
223 self::update();
224 }
225
226 /**
227 * Run an update callback when triggered by ActionScheduler.
228 *
229 * @since 3.6.0
230 * @param string $callback Callback name.
231 */
232 public static function run_update_callback( $callback ) {
233 include_once dirname( __FILE__ ) . '/wc-update-functions.php';
234
235 if ( is_callable( $callback ) ) {
236 self::run_update_callback_start( $callback );
237 $result = (bool) call_user_func( $callback );
238 self::run_update_callback_end( $callback, $result );
239 }
240 }
241
242 /**
243 * Triggered when a callback will run.
244 *
245 * @since 3.6.0
246 * @param string $callback Callback name.
247 */
248 protected static function run_update_callback_start( $callback ) {
249 wc_maybe_define_constant( 'WC_UPDATING', true );
250 }
251
252 /**
253 * Triggered when a callback has ran.
254 *
255 * @since 3.6.0
256 * @param string $callback Callback name.
257 * @param bool $result Return value from callback. Non-false need to run again.
258 */
259 protected static function run_update_callback_end( $callback, $result ) {
260 if ( $result ) {
261 WC()->queue()->add(
262 'woocommerce_run_update_callback',
263 array(
264 'update_callback' => $callback,
265 ),
266 'woocommerce-db-updates'
267 );
268 }
269 }
270
271 /**
272 * Install actions when a update button is clicked within the admin area.
273 *
274 * This function is hooked into admin_init to affect admin only.
275 */
276 public static function install_actions() {
277 if ( ! empty( $_GET['do_update_woocommerce'] ) ) { // WPCS: input var ok.
278 check_admin_referer( 'wc_db_update', 'wc_db_update_nonce' );
279 self::update();
280 WC_Admin_Notices::add_notice( 'update', true );
281 }
282 }
283
284 /**
285 * Install WC.
286 */
287 public static function install() {
288 if ( ! is_blog_installed() ) {
289 return;
290 }
291
292 // Check if we are not already running this routine.
293 if ( 'yes' === get_transient( 'wc_installing' ) ) {
294 return;
295 }
296
297 // If we made it till here nothing is running yet, lets set the transient now.
298 set_transient( 'wc_installing', 'yes', MINUTE_IN_SECONDS * 10 );
299 wc_maybe_define_constant( 'WC_INSTALLING', true );
300
301 WC()->wpdb_table_fix();
302 self::remove_admin_notices();
303 self::create_tables();
304 self::verify_base_tables();
305 self::create_options();
306 self::create_roles();
307 self::setup_environment();
308 self::create_terms();
309 self::create_cron_jobs();
310 self::create_files();
311 self::maybe_create_pages();
312 self::maybe_set_activation_transients();
313 self::update_wc_version();
314 self::maybe_update_db_version();
315
316 delete_transient( 'wc_installing' );
317
318 do_action( 'woocommerce_flush_rewrite_rules' );
319 do_action( 'woocommerce_installed' );
320 }
321
322 /**
323 * Check if all the base tables are present.
324 *
325 * @param bool $modify_notice Whether to modify notice based on if all tables are present.
326 * @param bool $execute Whether to execute get_schema queries as well.
327 *
328 * @return array List of querues.
329 */
330 public static function verify_base_tables( $modify_notice = true, $execute = false ) {
331 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
332
333 if ( $execute ) {
334 self::create_tables();
335 }
336 $queries = dbDelta( self::get_schema(), false );
337 $missing_tables = array();
338 foreach ( $queries as $table_name => $result ) {
339 if ( "Created table $table_name" === $result ) {
340 $missing_tables[] = $table_name;
341 }
342 }
343
344 if ( 0 < count( $missing_tables ) ) {
345 if ( $modify_notice ) {
346 WC_Admin_Notices::add_notice( 'base_tables_missing' );
347 }
348 update_option( 'woocommerce_schema_missing_tables', $missing_tables );
349 } else {
350 if ( $modify_notice ) {
351 WC_Admin_Notices::remove_notice( 'base_tables_missing' );
352 }
353 update_option( 'woocommerce_schema_version', WC()->db_version );
354 delete_option( 'woocommerce_schema_missing_tables' );
355 }
356 return $missing_tables;
357 }
358
359 /**
360 * Reset any notices added to admin.
361 *
362 * @since 3.2.0
363 */
364 private static function remove_admin_notices() {
365 include_once dirname( __FILE__ ) . '/admin/class-wc-admin-notices.php';
366 WC_Admin_Notices::remove_all_notices();
367 }
368
369 /**
370 * Setup WC environment - post types, taxonomies, endpoints.
371 *
372 * @since 3.2.0
373 */
374 private static function setup_environment() {
375 WC_Post_types::register_post_types();
376 WC_Post_types::register_taxonomies();
377 WC()->query->init_query_vars();
378 WC()->query->add_endpoints();
379 WC_API::add_endpoint();
380 WC_Auth::add_endpoint();
381 }
382
383 /**
384 * Is this a brand new WC install?
385 *
386 * A brand new install has no version yet. Also treat empty installs as 'new'.
387 *
388 * @since 3.2.0
389 * @return boolean
390 */
391 public static function is_new_install() {
392 $product_count = array_sum( (array) wp_count_posts( 'product' ) );
393
394 return is_null( get_option( 'woocommerce_version', null ) ) || ( 0 === $product_count && -1 === wc_get_page_id( 'shop' ) );
395 }
396
397 /**
398 * Is a DB update needed?
399 *
400 * @since 3.2.0
401 * @return boolean
402 */
403 public static function needs_db_update() {
404 $current_db_version = get_option( 'woocommerce_db_version', null );
405 $updates = self::get_db_update_callbacks();
406 $update_versions = array_keys( $updates );
407 usort( $update_versions, 'version_compare' );
408
409 return ! is_null( $current_db_version ) && version_compare( $current_db_version, end( $update_versions ), '<' );
410 }
411
412 /**
413 * See if we need to set redirect transients for activation or not.
414 *
415 * @since 4.6.0
416 */
417 private static function maybe_set_activation_transients() {
418 if ( self::is_new_install() ) {
419 set_transient( '_wc_activation_redirect', 1, 30 );
420 }
421 }
422
423 /**
424 * See if we need to show or run database updates during install.
425 *
426 * @since 3.2.0
427 */
428 private static function maybe_update_db_version() {
429 if ( self::needs_db_update() ) {
430 if ( apply_filters( 'woocommerce_enable_auto_update_db', false ) ) {
431 self::update();
432 } else {
433 WC_Admin_Notices::add_notice( 'update', true );
434 }
435 } else {
436 self::update_db_version();
437 }
438 }
439
440 /**
441 * Update WC version to current.
442 */
443 private static function update_wc_version() {
444 delete_option( 'woocommerce_version' );
445 add_option( 'woocommerce_version', WC()->version );
446 }
447
448 /**
449 * Get list of DB update callbacks.
450 *
451 * @since 3.0.0
452 * @return array
453 */
454 public static function get_db_update_callbacks() {
455 return self::$db_updates;
456 }
457
458 /**
459 * Push all needed DB updates to the queue for processing.
460 */
461 private static function update() {
462 $current_db_version = get_option( 'woocommerce_db_version' );
463 $loop = 0;
464
465 foreach ( self::get_db_update_callbacks() as $version => $update_callbacks ) {
466 if ( version_compare( $current_db_version, $version, '<' ) ) {
467 foreach ( $update_callbacks as $update_callback ) {
468 WC()->queue()->schedule_single(
469 time() + $loop,
470 'woocommerce_run_update_callback',
471 array(
472 'update_callback' => $update_callback,
473 ),
474 'woocommerce-db-updates'
475 );
476 $loop++;
477 }
478 }
479 }
480 }
481
482 /**
483 * Update DB version to current.
484 *
485 * @param string|null $version New WooCommerce DB version or null.
486 */
487 public static function update_db_version( $version = null ) {
488 delete_option( 'woocommerce_db_version' );
489 add_option( 'woocommerce_db_version', is_null( $version ) ? WC()->version : $version );
490 }
491
492 /**
493 * Add more cron schedules.
494 *
495 * @param array $schedules List of WP scheduled cron jobs.
496 *
497 * @return array
498 */
499 public static function cron_schedules( $schedules ) {
500 $schedules['monthly'] = array(
501 'interval' => 2635200,
502 'display' => __( 'Monthly', 'woocommerce' ),
503 );
504 $schedules['fifteendays'] = array(
505 'interval' => 1296000,
506 'display' => __( 'Every 15 Days', 'woocommerce' ),
507 );
508 return $schedules;
509 }
510
511 /**
512 * Create cron jobs (clear them first).
513 */
514 private static function create_cron_jobs() {
515 wp_clear_scheduled_hook( 'woocommerce_scheduled_sales' );
516 wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
517 wp_clear_scheduled_hook( 'woocommerce_cleanup_sessions' );
518 wp_clear_scheduled_hook( 'woocommerce_cleanup_personal_data' );
519 wp_clear_scheduled_hook( 'woocommerce_cleanup_logs' );
520 wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
521 wp_clear_scheduled_hook( 'woocommerce_tracker_send_event' );
522
523 $ve = get_option( 'gmt_offset' ) > 0 ? '-' : '+';
524
525 wp_schedule_event( strtotime( '00:00 tomorrow ' . $ve . absint( get_option( 'gmt_offset' ) ) . ' HOURS' ), 'daily', 'woocommerce_scheduled_sales' );
526
527 $held_duration = get_option( 'woocommerce_hold_stock_minutes', '60' );
528
529 if ( '' !== $held_duration ) {
530 wp_schedule_single_event( time() + ( absint( $held_duration ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
531 }
532
533 // Delay the first run of `woocommerce_cleanup_personal_data` by 10 seconds
534 // so it doesn't occur in the same request. WooCommerce Admin also schedules
535 // a daily cron that gets lost due to a race condition. WC_Privacy's background
536 // processing instance updates the cron schedule from within a cron job.
537 wp_schedule_event( time() + 10, 'daily', 'woocommerce_cleanup_personal_data' );
538 wp_schedule_event( time() + ( 3 * HOUR_IN_SECONDS ), 'daily', 'woocommerce_cleanup_logs' );
539 wp_schedule_event( time() + ( 6 * HOUR_IN_SECONDS ), 'twicedaily', 'woocommerce_cleanup_sessions' );
540 wp_schedule_event( time() + MINUTE_IN_SECONDS, 'fifteendays', 'woocommerce_geoip_updater' );
541 wp_schedule_event( time() + 10, apply_filters( 'woocommerce_tracker_event_recurrence', 'daily' ), 'woocommerce_tracker_send_event' );
542 }
543
544 /**
545 * Create pages on installation.
546 */
547 public static function maybe_create_pages() {
548 if ( empty( get_option( 'woocommerce_db_version' ) ) ) {
549 self::create_pages();
550 }
551 }
552
553 /**
554 * Create pages that the plugin relies on, storing page IDs in variables.
555 */
556 public static function create_pages() {
557 include_once dirname( __FILE__ ) . '/admin/wc-admin-functions.php';
558
559 $pages = apply_filters(
560 'woocommerce_create_pages',
561 array(
562 'shop' => array(
563 'name' => _x( 'shop', 'Page slug', 'woocommerce' ),
564 'title' => _x( 'Shop', 'Page title', 'woocommerce' ),
565 'content' => '',
566 ),
567 'cart' => array(
568 'name' => _x( 'cart', 'Page slug', 'woocommerce' ),
569 'title' => _x( 'Cart', 'Page title', 'woocommerce' ),
570 'content' => '<!-- wp:shortcode -->[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']<!-- /wp:shortcode -->',
571 ),
572 'checkout' => array(
573 'name' => _x( 'checkout', 'Page slug', 'woocommerce' ),
574 'title' => _x( 'Checkout', 'Page title', 'woocommerce' ),
575 'content' => '<!-- wp:shortcode -->[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']<!-- /wp:shortcode -->',
576 ),
577 'myaccount' => array(
578 'name' => _x( 'my-account', 'Page slug', 'woocommerce' ),
579 'title' => _x( 'My account', 'Page title', 'woocommerce' ),
580 'content' => '<!-- wp:shortcode -->[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']<!-- /wp:shortcode -->',
581 ),
582 )
583 );
584
585 foreach ( $pages as $key => $page ) {
586 wc_create_page( esc_sql( $page['name'] ), 'woocommerce_' . $key . '_page_id', $page['title'], $page['content'], ! empty( $page['parent'] ) ? wc_get_page_id( $page['parent'] ) : '' );
587 }
588 }
589
590 /**
591 * Default options.
592 *
593 * Sets up the default options used on the settings page.
594 */
595 private static function create_options() {
596 // Include settings so that we can run through defaults.
597 include_once dirname( __FILE__ ) . '/admin/class-wc-admin-settings.php';
598
599 $settings = WC_Admin_Settings::get_settings_pages();
600
601 foreach ( $settings as $section ) {
602 if ( ! method_exists( $section, 'get_settings' ) ) {
603 continue;
604 }
605 $subsections = array_unique( array_merge( array( '' ), array_keys( $section->get_sections() ) ) );
606
607 foreach ( $subsections as $subsection ) {
608 foreach ( $section->get_settings( $subsection ) as $value ) {
609 if ( isset( $value['default'] ) && isset( $value['id'] ) ) {
610 $autoload = isset( $value['autoload'] ) ? (bool) $value['autoload'] : true;
611 add_option( $value['id'], $value['default'], '', ( $autoload ? 'yes' : 'no' ) );
612 }
613 }
614 }
615 }
616
617 // Define other defaults if not in setting screens.
618 add_option( 'woocommerce_single_image_width', '600', '', 'yes' );
619 add_option( 'woocommerce_thumbnail_image_width', '300', '', 'yes' );
620 add_option( 'woocommerce_checkout_highlight_required_fields', 'yes', '', 'yes' );
621 add_option( 'woocommerce_demo_store', 'no', '', 'no' );
622
623 // Define initial tax classes.
624 WC_Tax::create_tax_class( __( 'Reduced rate', 'woocommerce' ) );
625 WC_Tax::create_tax_class( __( 'Zero rate', 'woocommerce' ) );
626 }
627
628 /**
629 * Add the default terms for WC taxonomies - product types and order statuses. Modify this at your own risk.
630 */
631 public static function create_terms() {
632 $taxonomies = array(
633 'product_type' => array(
634 'simple',
635 'grouped',
636 'variable',
637 'external',
638 ),
639 'product_visibility' => array(
640 'exclude-from-search',
641 'exclude-from-catalog',
642 'featured',
643 'outofstock',
644 'rated-1',
645 'rated-2',
646 'rated-3',
647 'rated-4',
648 'rated-5',
649 ),
650 );
651
652 foreach ( $taxonomies as $taxonomy => $terms ) {
653 foreach ( $terms as $term ) {
654 if ( ! get_term_by( 'name', $term, $taxonomy ) ) { // @codingStandardsIgnoreLine.
655 wp_insert_term( $term, $taxonomy );
656 }
657 }
658 }
659
660 $woocommerce_default_category = (int) get_option( 'default_product_cat', 0 );
661
662 if ( ! $woocommerce_default_category || ! term_exists( $woocommerce_default_category, 'product_cat' ) ) {
663 $default_product_cat_id = 0;
664 $default_product_cat_slug = sanitize_title( _x( 'Uncategorized', 'Default category slug', 'woocommerce' ) );
665 $default_product_cat = get_term_by( 'slug', $default_product_cat_slug, 'product_cat' ); // @codingStandardsIgnoreLine.
666
667 if ( $default_product_cat ) {
668 $default_product_cat_id = absint( $default_product_cat->term_taxonomy_id );
669 } else {
670 $result = wp_insert_term( _x( 'Uncategorized', 'Default category slug', 'woocommerce' ), 'product_cat', array( 'slug' => $default_product_cat_slug ) );
671
672 if ( ! is_wp_error( $result ) && ! empty( $result['term_taxonomy_id'] ) ) {
673 $default_product_cat_id = absint( $result['term_taxonomy_id'] );
674 }
675 }
676
677 if ( $default_product_cat_id ) {
678 update_option( 'default_product_cat', $default_product_cat_id );
679 }
680 }
681 }
682
683 /**
684 * Set up the database tables which the plugin needs to function.
685 * WARNING: If you are modifying this method, make sure that its safe to call regardless of the state of database.
686 *
687 * 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`.
688 *
689 * TODO: Add all crucial tables that we have created from workers in the past.
690 *
691 * Tables:
692 * woocommerce_attribute_taxonomies - Table for storing attribute taxonomies - these are user defined
693 * woocommerce_downloadable_product_permissions - Table for storing user and guest download permissions.
694 * KEY(order_id, product_id, download_id) used for organizing downloads on the My Account page
695 * woocommerce_order_items - Order line items are stored in a table to make them easily queryable for reports
696 * woocommerce_order_itemmeta - Order line item meta is stored in a table for storing extra data.
697 * woocommerce_tax_rates - Tax Rates are stored inside 2 tables making tax queries simple and efficient.
698 * woocommerce_tax_rate_locations - Each rate can be applied to more than one postcode/city hence the second table.
699 */
700 private static function create_tables() {
701 global $wpdb;
702
703 $wpdb->hide_errors();
704
705 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
706
707 /**
708 * Before updating with DBDELTA, remove any primary keys which could be
709 * modified due to schema updates.
710 */
711 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_downloadable_product_permissions';" ) ) {
712 if ( ! $wpdb->get_var( "SHOW COLUMNS FROM `{$wpdb->prefix}woocommerce_downloadable_product_permissions` LIKE 'permission_id';" ) ) {
713 $wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions DROP PRIMARY KEY, ADD `permission_id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT;" );
714 }
715 }
716
717 /**
718 * Change wp_woocommerce_sessions schema to use a bigint auto increment field instead of char(32) field as
719 * 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
720 * there were reports of issues with this table (see https://github.com/woocommerce/woocommerce/issues/20912).
721 *
722 * This query needs to run before dbDelta() as this WP function is not able to handle primary key changes
723 * (see https://github.com/woocommerce/woocommerce/issues/21534 and https://core.trac.wordpress.org/ticket/40357).
724 */
725 if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_sessions'" ) ) {
726 if ( ! $wpdb->get_var( "SHOW KEYS FROM {$wpdb->prefix}woocommerce_sessions WHERE Key_name = 'PRIMARY' AND Column_name = 'session_id'" ) ) {
727 $wpdb->query(
728 "ALTER TABLE `{$wpdb->prefix}woocommerce_sessions` DROP PRIMARY KEY, DROP KEY `session_id`, ADD PRIMARY KEY(`session_id`), ADD UNIQUE KEY(`session_key`)"
729 );
730 }
731 }
732
733 dbDelta( self::get_schema() );
734
735 $index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE column_name = 'comment_type' and key_name = 'woo_idx_comment_type'" );
736
737 if ( is_null( $index_exists ) ) {
738 // Add an index to the field comment_type to improve the response time of the query
739 // used by WC_Comments::wp_count_comments() to get the number of comments by type.
740 $wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_type (comment_type)" );
741 }
742
743 // Get tables data types and check it matches before adding constraint.
744 $download_log_columns = $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}wc_download_log WHERE Field = 'permission_id'", ARRAY_A );
745 $download_log_column_type = '';
746 if ( isset( $download_log_columns[0]['Type'] ) ) {
747 $download_log_column_type = $download_log_columns[0]['Type'];
748 }
749
750 $download_permissions_columns = $wpdb->get_results( "SHOW COLUMNS FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions WHERE Field = 'permission_id'", ARRAY_A );
751 $download_permissions_column_type = '';
752 if ( isset( $download_permissions_columns[0]['Type'] ) ) {
753 $download_permissions_column_type = $download_permissions_columns[0]['Type'];
754 }
755
756 // Add constraint to download logs if the columns matches.
757 if ( ! empty( $download_permissions_column_type ) && ! empty( $download_log_column_type ) && $download_permissions_column_type === $download_log_column_type ) {
758 $fk_result = $wpdb->get_row( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log" ); // WPCS: unprepared SQL ok.
759 if ( false === strpos( $fk_result->{'Create Table'}, "fk_{$wpdb->prefix}wc_download_log_permission_id" ) ) {
760 $wpdb->query(
761 "ALTER TABLE `{$wpdb->prefix}wc_download_log`
762 ADD CONSTRAINT `fk_{$wpdb->prefix}wc_download_log_permission_id`
763 FOREIGN KEY (`permission_id`)
764 REFERENCES `{$wpdb->prefix}woocommerce_downloadable_product_permissions` (`permission_id`) ON DELETE CASCADE;"
765 );
766 }
767 }
768
769 // Clear table caches.
770 delete_transient( 'wc_attribute_taxonomies' );
771 }
772
773 /**
774 * Get Table schema.
775 *
776 * See https://github.com/woocommerce/woocommerce/wiki/Database-Description/
777 *
778 * A note on indexes; Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
779 * As of WordPress 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which
780 * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
781 *
782 * Changing indexes may cause duplicate index notices in logs due to https://core.trac.wordpress.org/ticket/34870 but dropping
783 * indexes first causes too much load on some servers/larger DB.
784 *
785 * When adding or removing a table, make sure to update the list of tables in WC_Install::get_tables().
786 *
787 * @return string
788 */
789 private static function get_schema() {
790 global $wpdb;
791
792 $collate = '';
793
794 if ( $wpdb->has_cap( 'collation' ) ) {
795 $collate = $wpdb->get_charset_collate();
796 }
797
798 /*
799 * Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
800 * As of WP 4.2, however, they moved to utf8mb4, which uses 4 bytes per character. This means that an index which
801 * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
802 */
803 $max_index_length = 191;
804
805 $tables = "
806 CREATE TABLE {$wpdb->prefix}woocommerce_sessions (
807 session_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
808 session_key char(32) NOT NULL,
809 session_value longtext NOT NULL,
810 session_expiry BIGINT UNSIGNED NOT NULL,
811 PRIMARY KEY (session_id),
812 UNIQUE KEY session_key (session_key)
813 ) $collate;
814 CREATE TABLE {$wpdb->prefix}woocommerce_api_keys (
815 key_id BIGINT UNSIGNED NOT NULL auto_increment,
816 user_id BIGINT UNSIGNED NOT NULL,
817 description varchar(200) NULL,
818 permissions varchar(10) NOT NULL,
819 consumer_key char(64) NOT NULL,
820 consumer_secret char(43) NOT NULL,
821 nonces longtext NULL,
822 truncated_key char(7) NOT NULL,
823 last_access datetime NULL default null,
824 PRIMARY KEY (key_id),
825 KEY consumer_key (consumer_key),
826 KEY consumer_secret (consumer_secret)
827 ) $collate;
828 CREATE TABLE {$wpdb->prefix}woocommerce_attribute_taxonomies (
829 attribute_id BIGINT UNSIGNED NOT NULL auto_increment,
830 attribute_name varchar(200) NOT NULL,
831 attribute_label varchar(200) NULL,
832 attribute_type varchar(20) NOT NULL,
833 attribute_orderby varchar(20) NOT NULL,
834 attribute_public int(1) NOT NULL DEFAULT 1,
835 PRIMARY KEY (attribute_id),
836 KEY attribute_name (attribute_name(20))
837 ) $collate;
838 CREATE TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions (
839 permission_id BIGINT UNSIGNED NOT NULL auto_increment,
840 download_id varchar(36) NOT NULL,
841 product_id BIGINT UNSIGNED NOT NULL,
842 order_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
843 order_key varchar(200) NOT NULL,
844 user_email varchar(200) NOT NULL,
845 user_id BIGINT UNSIGNED NULL,
846 downloads_remaining varchar(9) NULL,
847 access_granted datetime NOT NULL default '0000-00-00 00:00:00',
848 access_expires datetime NULL default null,
849 download_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
850 PRIMARY KEY (permission_id),
851 KEY download_order_key_product (product_id,order_id,order_key(16),download_id),
852 KEY download_order_product (download_id,order_id,product_id),
853 KEY order_id (order_id),
854 KEY user_order_remaining_expires (user_id,order_id,downloads_remaining,access_expires)
855 ) $collate;
856 CREATE TABLE {$wpdb->prefix}woocommerce_order_items (
857 order_item_id BIGINT UNSIGNED NOT NULL auto_increment,
858 order_item_name TEXT NOT NULL,
859 order_item_type varchar(200) NOT NULL DEFAULT '',
860 order_id BIGINT UNSIGNED NOT NULL,
861 PRIMARY KEY (order_item_id),
862 KEY order_id (order_id)
863 ) $collate;
864 CREATE TABLE {$wpdb->prefix}woocommerce_order_itemmeta (
865 meta_id BIGINT UNSIGNED NOT NULL auto_increment,
866 order_item_id BIGINT UNSIGNED NOT NULL,
867 meta_key varchar(255) default NULL,
868 meta_value longtext NULL,
869 PRIMARY KEY (meta_id),
870 KEY order_item_id (order_item_id),
871 KEY meta_key (meta_key(32))
872 ) $collate;
873 CREATE TABLE {$wpdb->prefix}woocommerce_tax_rates (
874 tax_rate_id BIGINT UNSIGNED NOT NULL auto_increment,
875 tax_rate_country varchar(2) NOT NULL DEFAULT '',
876 tax_rate_state varchar(200) NOT NULL DEFAULT '',
877 tax_rate varchar(8) NOT NULL DEFAULT '',
878 tax_rate_name varchar(200) NOT NULL DEFAULT '',
879 tax_rate_priority BIGINT UNSIGNED NOT NULL,
880 tax_rate_compound int(1) NOT NULL DEFAULT 0,
881 tax_rate_shipping int(1) NOT NULL DEFAULT 1,
882 tax_rate_order BIGINT UNSIGNED NOT NULL,
883 tax_rate_class varchar(200) NOT NULL DEFAULT '',
884 PRIMARY KEY (tax_rate_id),
885 KEY tax_rate_country (tax_rate_country),
886 KEY tax_rate_state (tax_rate_state(2)),
887 KEY tax_rate_class (tax_rate_class(10)),
888 KEY tax_rate_priority (tax_rate_priority)
889 ) $collate;
890 CREATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations (
891 location_id BIGINT UNSIGNED NOT NULL auto_increment,
892 location_code varchar(200) NOT NULL,
893 tax_rate_id BIGINT UNSIGNED NOT NULL,
894 location_type varchar(40) NOT NULL,
895 PRIMARY KEY (location_id),
896 KEY tax_rate_id (tax_rate_id),
897 KEY location_type_code (location_type(10),location_code(20))
898 ) $collate;
899 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zones (
900 zone_id BIGINT UNSIGNED NOT NULL auto_increment,
901 zone_name varchar(200) NOT NULL,
902 zone_order BIGINT UNSIGNED NOT NULL,
903 PRIMARY KEY (zone_id)
904 ) $collate;
905 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_locations (
906 location_id BIGINT UNSIGNED NOT NULL auto_increment,
907 zone_id BIGINT UNSIGNED NOT NULL,
908 location_code varchar(200) NOT NULL,
909 location_type varchar(40) NOT NULL,
910 PRIMARY KEY (location_id),
911 KEY location_id (location_id),
912 KEY location_type_code (location_type(10),location_code(20))
913 ) $collate;
914 CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_methods (
915 zone_id BIGINT UNSIGNED NOT NULL,
916 instance_id BIGINT UNSIGNED NOT NULL auto_increment,
917 method_id varchar(200) NOT NULL,
918 method_order BIGINT UNSIGNED NOT NULL,
919 is_enabled tinyint(1) NOT NULL DEFAULT '1',
920 PRIMARY KEY (instance_id)
921 ) $collate;
922 CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokens (
923 token_id BIGINT UNSIGNED NOT NULL auto_increment,
924 gateway_id varchar(200) NOT NULL,
925 token text NOT NULL,
926 user_id BIGINT UNSIGNED NOT NULL DEFAULT '0',
927 type varchar(200) NOT NULL,
928 is_default tinyint(1) NOT NULL DEFAULT '0',
929 PRIMARY KEY (token_id),
930 KEY user_id (user_id)
931 ) $collate;
932 CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokenmeta (
933 meta_id BIGINT UNSIGNED NOT NULL auto_increment,
934 payment_token_id BIGINT UNSIGNED NOT NULL,
935 meta_key varchar(255) NULL,
936 meta_value longtext NULL,
937 PRIMARY KEY (meta_id),
938 KEY payment_token_id (payment_token_id),
939 KEY meta_key (meta_key(32))
940 ) $collate;
941 CREATE TABLE {$wpdb->prefix}woocommerce_log (
942 log_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
943 timestamp datetime NOT NULL,
944 level smallint(4) NOT NULL,
945 source varchar(200) NOT NULL,
946 message longtext NOT NULL,
947 context longtext NULL,
948 PRIMARY KEY (log_id),
949 KEY level (level)
950 ) $collate;
951 CREATE TABLE {$wpdb->prefix}wc_webhooks (
952 webhook_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
953 status varchar(200) NOT NULL,
954 name text NOT NULL,
955 user_id BIGINT UNSIGNED NOT NULL,
956 delivery_url text NOT NULL,
957 secret text NOT NULL,
958 topic varchar(200) NOT NULL,
959 date_created datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
960 date_created_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
961 date_modified datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
962 date_modified_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
963 api_version smallint(4) NOT NULL,
964 failure_count smallint(10) NOT NULL DEFAULT '0',
965 pending_delivery tinyint(1) NOT NULL DEFAULT '0',
966 PRIMARY KEY (webhook_id),
967 KEY user_id (user_id)
968 ) $collate;
969 CREATE TABLE {$wpdb->prefix}wc_download_log (
970 download_log_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
971 timestamp datetime NOT NULL,
972 permission_id BIGINT UNSIGNED NOT NULL,
973 user_id BIGINT UNSIGNED NULL,
974 user_ip_address VARCHAR(100) NULL DEFAULT '',
975 PRIMARY KEY (download_log_id),
976 KEY permission_id (permission_id),
977 KEY timestamp (timestamp)
978 ) $collate;
979 CREATE TABLE {$wpdb->prefix}wc_product_meta_lookup (
980 `product_id` bigint(20) NOT NULL,
981 `sku` varchar(100) NULL default '',
982 `virtual` tinyint(1) NULL default 0,
983 `downloadable` tinyint(1) NULL default 0,
984 `min_price` decimal(19,4) NULL default NULL,
985 `max_price` decimal(19,4) NULL default NULL,
986 `onsale` tinyint(1) NULL default 0,
987 `stock_quantity` double NULL default NULL,
988 `stock_status` varchar(100) NULL default 'instock',
989 `rating_count` bigint(20) NULL default 0,
990 `average_rating` decimal(3,2) NULL default 0.00,
991 `total_sales` bigint(20) NULL default 0,
992 `tax_status` varchar(100) NULL default 'taxable',
993 `tax_class` varchar(100) NULL default '',
994 PRIMARY KEY (`product_id`),
995 KEY `virtual` (`virtual`),
996 KEY `downloadable` (`downloadable`),
997 KEY `stock_status` (`stock_status`),
998 KEY `stock_quantity` (`stock_quantity`),
999 KEY `onsale` (`onsale`),
1000 KEY min_max_price (`min_price`, `max_price`)
1001 ) $collate;
1002 CREATE TABLE {$wpdb->prefix}wc_tax_rate_classes (
1003 tax_rate_class_id BIGINT UNSIGNED NOT NULL auto_increment,
1004 name varchar(200) NOT NULL DEFAULT '',
1005 slug varchar(200) NOT NULL DEFAULT '',
1006 PRIMARY KEY (tax_rate_class_id),
1007 UNIQUE KEY slug (slug($max_index_length))
1008 ) $collate;
1009 CREATE TABLE {$wpdb->prefix}wc_reserved_stock (
1010 `order_id` bigint(20) NOT NULL,
1011 `product_id` bigint(20) NOT NULL,
1012 `stock_quantity` double NOT NULL DEFAULT 0,
1013 `timestamp` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1014 `expires` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
1015 PRIMARY KEY (`order_id`, `product_id`)
1016 ) $collate;
1017 ";
1018
1019 return $tables;
1020 }
1021
1022 /**
1023 * Return a list of WooCommerce tables. Used to make sure all WC tables are dropped when uninstalling the plugin
1024 * in a single site or multi site environment.
1025 *
1026 * @return array WC tables.
1027 */
1028 public static function get_tables() {
1029 global $wpdb;
1030
1031 $tables = array(
1032 "{$wpdb->prefix}wc_download_log",
1033 "{$wpdb->prefix}wc_product_meta_lookup",
1034 "{$wpdb->prefix}wc_tax_rate_classes",
1035 "{$wpdb->prefix}wc_webhooks",
1036 "{$wpdb->prefix}woocommerce_api_keys",
1037 "{$wpdb->prefix}woocommerce_attribute_taxonomies",
1038 "{$wpdb->prefix}woocommerce_downloadable_product_permissions",
1039 "{$wpdb->prefix}woocommerce_log",
1040 "{$wpdb->prefix}woocommerce_order_itemmeta",
1041 "{$wpdb->prefix}woocommerce_order_items",
1042 "{$wpdb->prefix}woocommerce_payment_tokenmeta",
1043 "{$wpdb->prefix}woocommerce_payment_tokens",
1044 "{$wpdb->prefix}woocommerce_sessions",
1045 "{$wpdb->prefix}woocommerce_shipping_zone_locations",
1046 "{$wpdb->prefix}woocommerce_shipping_zone_methods",
1047 "{$wpdb->prefix}woocommerce_shipping_zones",
1048 "{$wpdb->prefix}woocommerce_tax_rate_locations",
1049 "{$wpdb->prefix}woocommerce_tax_rates",
1050 "{$wpdb->prefix}wc_reserved_stock",
1051 );
1052
1053 /**
1054 * Filter the list of known WooCommerce tables.
1055 *
1056 * If WooCommerce plugins need to add new tables, they can inject them here.
1057 *
1058 * @param array $tables An array of WooCommerce-specific database table names.
1059 */
1060 $tables = apply_filters( 'woocommerce_install_get_tables', $tables );
1061
1062 return $tables;
1063 }
1064
1065 /**
1066 * Drop WooCommerce tables.
1067 *
1068 * @return void
1069 */
1070 public static function drop_tables() {
1071 global $wpdb;
1072
1073 $tables = self::get_tables();
1074
1075 foreach ( $tables as $table ) {
1076 $wpdb->query( "DROP TABLE IF EXISTS {$table}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1077 }
1078 }
1079
1080 /**
1081 * Uninstall tables when MU blog is deleted.
1082 *
1083 * @param array $tables List of tables that will be deleted by WP.
1084 *
1085 * @return string[]
1086 */
1087 public static function wpmu_drop_tables( $tables ) {
1088 return array_merge( $tables, self::get_tables() );
1089 }
1090
1091 /**
1092 * Create roles and capabilities.
1093 */
1094 public static function create_roles() {
1095 global $wp_roles;
1096
1097 if ( ! class_exists( 'WP_Roles' ) ) {
1098 return;
1099 }
1100
1101 if ( ! isset( $wp_roles ) ) {
1102 $wp_roles = new WP_Roles(); // @codingStandardsIgnoreLine
1103 }
1104
1105 // Dummy gettext calls to get strings in the catalog.
1106 /* translators: user role */
1107 _x( 'Customer', 'User role', 'woocommerce' );
1108 /* translators: user role */
1109 _x( 'Shop manager', 'User role', 'woocommerce' );
1110
1111 // Customer role.
1112 add_role(
1113 'customer',
1114 'Customer',
1115 array(
1116 'read' => true,
1117 )
1118 );
1119
1120 // Shop manager role.
1121 add_role(
1122 'shop_manager',
1123 'Shop manager',
1124 array(
1125 'level_9' => true,
1126 'level_8' => true,
1127 'level_7' => true,
1128 'level_6' => true,
1129 'level_5' => true,
1130 'level_4' => true,
1131 'level_3' => true,
1132 'level_2' => true,
1133 'level_1' => true,
1134 'level_0' => true,
1135 'read' => true,
1136 'read_private_pages' => true,
1137 'read_private_posts' => true,
1138 'edit_posts' => true,
1139 'edit_pages' => true,
1140 'edit_published_posts' => true,
1141 'edit_published_pages' => true,
1142 'edit_private_pages' => true,
1143 'edit_private_posts' => true,
1144 'edit_others_posts' => true,
1145 'edit_others_pages' => true,
1146 'publish_posts' => true,
1147 'publish_pages' => true,
1148 'delete_posts' => true,
1149 'delete_pages' => true,
1150 'delete_private_pages' => true,
1151 'delete_private_posts' => true,
1152 'delete_published_pages' => true,
1153 'delete_published_posts' => true,
1154 'delete_others_posts' => true,
1155 'delete_others_pages' => true,
1156 'manage_categories' => true,
1157 'manage_links' => true,
1158 'moderate_comments' => true,
1159 'upload_files' => true,
1160 'export' => true,
1161 'import' => true,
1162 'list_users' => true,
1163 'edit_theme_options' => true,
1164 )
1165 );
1166
1167 $capabilities = self::get_core_capabilities();
1168
1169 foreach ( $capabilities as $cap_group ) {
1170 foreach ( $cap_group as $cap ) {
1171 $wp_roles->add_cap( 'shop_manager', $cap );
1172 $wp_roles->add_cap( 'administrator', $cap );
1173 }
1174 }
1175 }
1176
1177 /**
1178 * Get capabilities for WooCommerce - these are assigned to admin/shop manager during installation or reset.
1179 *
1180 * @return array
1181 */
1182 public static function get_core_capabilities() {
1183 $capabilities = array();
1184
1185 $capabilities['core'] = array(
1186 'manage_woocommerce',
1187 'view_woocommerce_reports',
1188 );
1189
1190 $capability_types = array( 'product', 'shop_order', 'shop_coupon' );
1191
1192 foreach ( $capability_types as $capability_type ) {
1193
1194 $capabilities[ $capability_type ] = array(
1195 // Post type.
1196 "edit_{$capability_type}",
1197 "read_{$capability_type}",
1198 "delete_{$capability_type}",
1199 "edit_{$capability_type}s",
1200 "edit_others_{$capability_type}s",
1201 "publish_{$capability_type}s",
1202 "read_private_{$capability_type}s",
1203 "delete_{$capability_type}s",
1204 "delete_private_{$capability_type}s",
1205 "delete_published_{$capability_type}s",
1206 "delete_others_{$capability_type}s",
1207 "edit_private_{$capability_type}s",
1208 "edit_published_{$capability_type}s",
1209
1210 // Terms.
1211 "manage_{$capability_type}_terms",
1212 "edit_{$capability_type}_terms",
1213 "delete_{$capability_type}_terms",
1214 "assign_{$capability_type}_terms",
1215 );
1216 }
1217
1218 return $capabilities;
1219 }
1220
1221 /**
1222 * Remove WooCommerce roles.
1223 */
1224 public static function remove_roles() {
1225 global $wp_roles;
1226
1227 if ( ! class_exists( 'WP_Roles' ) ) {
1228 return;
1229 }
1230
1231 if ( ! isset( $wp_roles ) ) {
1232 $wp_roles = new WP_Roles(); // @codingStandardsIgnoreLine
1233 }
1234
1235 $capabilities = self::get_core_capabilities();
1236
1237 foreach ( $capabilities as $cap_group ) {
1238 foreach ( $cap_group as $cap ) {
1239 $wp_roles->remove_cap( 'shop_manager', $cap );
1240 $wp_roles->remove_cap( 'administrator', $cap );
1241 }
1242 }
1243
1244 remove_role( 'customer' );
1245 remove_role( 'shop_manager' );
1246 }
1247
1248 /**
1249 * Create files/directories.
1250 */
1251 private static function create_files() {
1252 // Bypass if filesystem is read-only and/or non-standard upload system is used.
1253 if ( apply_filters( 'woocommerce_install_skip_create_files', false ) ) {
1254 return;
1255 }
1256
1257 // Install files and folders for uploading files and prevent hotlinking.
1258 $upload_dir = wp_get_upload_dir();
1259 $download_method = get_option( 'woocommerce_file_download_method', 'force' );
1260
1261 $files = array(
1262 array(
1263 'base' => $upload_dir['basedir'] . '/woocommerce_uploads',
1264 'file' => 'index.html',
1265 'content' => '',
1266 ),
1267 array(
1268 'base' => WC_LOG_DIR,
1269 'file' => '.htaccess',
1270 'content' => 'deny from all',
1271 ),
1272 array(
1273 'base' => WC_LOG_DIR,
1274 'file' => 'index.html',
1275 'content' => '',
1276 ),
1277 array(
1278 'base' => $upload_dir['basedir'] . '/woocommerce_uploads',
1279 'file' => '.htaccess',
1280 'content' => 'redirect' === $download_method ? 'Options -Indexes' : 'deny from all',
1281 ),
1282 );
1283
1284 foreach ( $files as $file ) {
1285 if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
1286 $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'wb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen
1287 if ( $file_handle ) {
1288 fwrite( $file_handle, $file['content'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
1289 fclose( $file_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
1290 }
1291 }
1292 }
1293
1294 // Create attachment for placeholders.
1295 self::create_placeholder_image();
1296 }
1297
1298 /**
1299 * Create a placeholder image in the media library.
1300 *
1301 * @since 3.5.0
1302 */
1303 private static function create_placeholder_image() {
1304 $placeholder_image = get_option( 'woocommerce_placeholder_image', 0 );
1305
1306 // Validate current setting if set. If set, return.
1307 if ( ! empty( $placeholder_image ) ) {
1308 if ( ! is_numeric( $placeholder_image ) ) {
1309 return;
1310 } elseif ( $placeholder_image && wp_attachment_is_image( $placeholder_image ) ) {
1311 return;
1312 }
1313 }
1314
1315 $upload_dir = wp_upload_dir();
1316 $source = WC()->plugin_path() . '/assets/images/placeholder-attachment.png';
1317 $filename = $upload_dir['basedir'] . '/woocommerce-placeholder.png';
1318
1319 if ( ! file_exists( $filename ) ) {
1320 copy( $source, $filename ); // @codingStandardsIgnoreLine.
1321 }
1322
1323 if ( ! file_exists( $filename ) ) {
1324 update_option( 'woocommerce_placeholder_image', 0 );
1325 return;
1326 }
1327
1328 $filetype = wp_check_filetype( basename( $filename ), null );
1329 $attachment = array(
1330 'guid' => $upload_dir['url'] . '/' . basename( $filename ),
1331 'post_mime_type' => $filetype['type'],
1332 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $filename ) ),
1333 'post_content' => '',
1334 'post_status' => 'inherit',
1335 );
1336 $attach_id = wp_insert_attachment( $attachment, $filename );
1337
1338 update_option( 'woocommerce_placeholder_image', $attach_id );
1339
1340 // Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
1341 require_once ABSPATH . 'wp-admin/includes/image.php';
1342
1343 // Generate the metadata for the attachment, and update the database record.
1344 $attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
1345 wp_update_attachment_metadata( $attach_id, $attach_data );
1346 }
1347
1348 /**
1349 * Show action links on the plugin screen.
1350 *
1351 * @param mixed $links Plugin Action links.
1352 *
1353 * @return array
1354 */
1355 public static function plugin_action_links( $links ) {
1356 $action_links = array(
1357 'settings' => '<a href="' . admin_url( 'admin.php?page=wc-settings' ) . '" aria-label="' . esc_attr__( 'View WooCommerce settings', 'woocommerce' ) . '">' . esc_html__( 'Settings', 'woocommerce' ) . '</a>',
1358 );
1359
1360 return array_merge( $action_links, $links );
1361 }
1362
1363 /**
1364 * Show row meta on the plugin screen.
1365 *
1366 * @param mixed $links Plugin Row Meta.
1367 * @param mixed $file Plugin Base file.
1368 *
1369 * @return array
1370 */
1371 public static function plugin_row_meta( $links, $file ) {
1372 if ( WC_PLUGIN_BASENAME !== $file ) {
1373 return $links;
1374 }
1375
1376 $row_meta = array(
1377 '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>',
1378 '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>',
1379 '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>',
1380 );
1381
1382 if ( WCConnectionHelper::is_connected() ) {
1383 $row_meta['premium_support'] = '<a href="' . esc_url( apply_filters( 'woocommerce_support_url', 'https://woocommerce.com/my-account/tickets/' ) ) . '" aria-label="' . esc_attr__( 'Visit premium customer support', 'woocommerce' ) . '">' . esc_html__( 'Premium support', 'woocommerce' ) . '</a>';
1384 }
1385
1386 return array_merge( $links, $row_meta );
1387 }
1388
1389 /**
1390 * Get slug from path and associate it with the path.
1391 *
1392 * @param array $plugins Associative array of plugin files to paths.
1393 * @param string $key Plugin relative path. Example: woocommerce/woocommerce.php.
1394 */
1395 private static function associate_plugin_file( $plugins, $key ) {
1396 $path = explode( '/', $key );
1397 $filename = end( $path );
1398 $plugins[ $filename ] = $key;
1399 return $plugins;
1400 }
1401
1402 /**
1403 * Install a plugin from .org in the background via a cron job (used by
1404 * installer - opt in).
1405 *
1406 * @param string $plugin_to_install_id Plugin ID.
1407 * @param array $plugin_to_install Plugin information.
1408 *
1409 * @throws Exception If unable to proceed with plugin installation.
1410 * @since 2.6.0
1411 */
1412 public static function background_installer( $plugin_to_install_id, $plugin_to_install ) {
1413 // Explicitly clear the event.
1414 $args = func_get_args();
1415
1416 if ( ! empty( $plugin_to_install['repo-slug'] ) ) {
1417 require_once ABSPATH . 'wp-admin/includes/file.php';
1418 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
1419 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1420 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1421
1422 WP_Filesystem();
1423
1424 $skin = new Automatic_Upgrader_Skin();
1425 $upgrader = new WP_Upgrader( $skin );
1426 $installed_plugins = array_reduce( array_keys( get_plugins() ), array( __CLASS__, 'associate_plugin_file' ) );
1427 if ( empty( $installed_plugins ) ) {
1428 $installed_plugins = array();
1429 }
1430 $plugin_slug = $plugin_to_install['repo-slug'];
1431 $plugin_file = isset( $plugin_to_install['file'] ) ? $plugin_to_install['file'] : $plugin_slug . '.php';
1432 $installed = false;
1433 $activate = false;
1434
1435 // See if the plugin is installed already.
1436 if ( isset( $installed_plugins[ $plugin_file ] ) ) {
1437 $installed = true;
1438 $activate = ! is_plugin_active( $installed_plugins[ $plugin_file ] );
1439 }
1440
1441 // Install this thing!
1442 if ( ! $installed ) {
1443 // Suppress feedback.
1444 ob_start();
1445
1446 try {
1447 $plugin_information = plugins_api(
1448 'plugin_information',
1449 array(
1450 'slug' => $plugin_slug,
1451 'fields' => array(
1452 'short_description' => false,
1453 'sections' => false,
1454 'requires' => false,
1455 'rating' => false,
1456 'ratings' => false,
1457 'downloaded' => false,
1458 'last_updated' => false,
1459 'added' => false,
1460 'tags' => false,
1461 'homepage' => false,
1462 'donate_link' => false,
1463 'author_profile' => false,
1464 'author' => false,
1465 ),
1466 )
1467 );
1468
1469 if ( is_wp_error( $plugin_information ) ) {
1470 throw new Exception( $plugin_information->get_error_message() );
1471 }
1472
1473 $package = $plugin_information->download_link;
1474 $download = $upgrader->download_package( $package );
1475
1476 if ( is_wp_error( $download ) ) {
1477 throw new Exception( $download->get_error_message() );
1478 }
1479
1480 $working_dir = $upgrader->unpack_package( $download, true );
1481
1482 if ( is_wp_error( $working_dir ) ) {
1483 throw new Exception( $working_dir->get_error_message() );
1484 }
1485
1486 $result = $upgrader->install_package(
1487 array(
1488 'source' => $working_dir,
1489 'destination' => WP_PLUGIN_DIR,
1490 'clear_destination' => false,
1491 'abort_if_destination_exists' => false,
1492 'clear_working' => true,
1493 'hook_extra' => array(
1494 'type' => 'plugin',
1495 'action' => 'install',
1496 ),
1497 )
1498 );
1499
1500 if ( is_wp_error( $result ) ) {
1501 throw new Exception( $result->get_error_message() );
1502 }
1503
1504 $activate = true;
1505
1506 } catch ( Exception $e ) {
1507 WC_Admin_Notices::add_custom_notice(
1508 $plugin_to_install_id . '_install_error',
1509 sprintf(
1510 // translators: 1: plugin name, 2: error message, 3: URL to install plugin manually.
1511 __( '%1$s could not be installed (%2$s). <a href="%3$s">Please install it manually by clicking here.</a>', 'woocommerce' ),
1512 $plugin_to_install['name'],
1513 $e->getMessage(),
1514 esc_url( admin_url( 'index.php?wc-install-plugin-redirect=' . $plugin_slug ) )
1515 )
1516 );
1517 }
1518
1519 // Discard feedback.
1520 ob_end_clean();
1521 }
1522
1523 wp_clean_plugins_cache();
1524
1525 // Activate this thing.
1526 if ( $activate ) {
1527 try {
1528 add_action( 'add_option_mailchimp_woocommerce_plugin_do_activation_redirect', array( __CLASS__, 'remove_mailchimps_redirect' ), 10, 2 );
1529 $result = activate_plugin( $installed ? $installed_plugins[ $plugin_file ] : $plugin_slug . '/' . $plugin_file );
1530
1531 if ( is_wp_error( $result ) ) {
1532 throw new Exception( $result->get_error_message() );
1533 }
1534 } catch ( Exception $e ) {
1535 WC_Admin_Notices::add_custom_notice(
1536 $plugin_to_install_id . '_install_error',
1537 sprintf(
1538 // translators: 1: plugin name, 2: URL to WP plugin page.
1539 __( '%1$s was installed but could not be activated. <a href="%2$s">Please activate it manually by clicking here.</a>', 'woocommerce' ),
1540 $plugin_to_install['name'],
1541 admin_url( 'plugins.php' )
1542 )
1543 );
1544 }
1545 }
1546 }
1547 }
1548
1549 /**
1550 * Removes redirect added during MailChimp plugin's activation.
1551 *
1552 * @param string $option Option name.
1553 * @param string $value Option value.
1554 */
1555 public static function remove_mailchimps_redirect( $option, $value ) {
1556 // Remove this action to prevent infinite looping.
1557 remove_action( 'add_option_mailchimp_woocommerce_plugin_do_activation_redirect', array( __CLASS__, 'remove_mailchimps_redirect' ) );
1558
1559 // Update redirect back to false.
1560 update_option( 'mailchimp_woocommerce_plugin_do_activation_redirect', false );
1561 }
1562
1563 /**
1564 * Install a theme from .org in the background via a cron job (used by installer - opt in).
1565 *
1566 * @param string $theme_slug Theme slug.
1567 *
1568 * @throws Exception If unable to proceed with theme installation.
1569 * @since 3.1.0
1570 */
1571 public static function theme_background_installer( $theme_slug ) {
1572 // Explicitly clear the event.
1573 $args = func_get_args();
1574
1575 if ( ! empty( $theme_slug ) ) {
1576 // Suppress feedback.
1577 ob_start();
1578
1579 try {
1580 $theme = wp_get_theme( $theme_slug );
1581
1582 if ( ! $theme->exists() ) {
1583 require_once ABSPATH . 'wp-admin/includes/file.php';
1584 include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1585 include_once ABSPATH . 'wp-admin/includes/theme.php';
1586
1587 WP_Filesystem();
1588
1589 $skin = new Automatic_Upgrader_Skin();
1590 $upgrader = new Theme_Upgrader( $skin );
1591 $api = themes_api(
1592 'theme_information',
1593 array(
1594 'slug' => $theme_slug,
1595 'fields' => array( 'sections' => false ),
1596 )
1597 );
1598 $result = $upgrader->install( $api->download_link );
1599
1600 if ( is_wp_error( $result ) ) {
1601 throw new Exception( $result->get_error_message() );
1602 } elseif ( is_wp_error( $skin->result ) ) {
1603 throw new Exception( $skin->result->get_error_message() );
1604 } elseif ( is_null( $result ) ) {
1605 throw new Exception( 'Unable to connect to the filesystem. Please confirm your credentials.' );
1606 }
1607 }
1608
1609 switch_theme( $theme_slug );
1610 } catch ( Exception $e ) {
1611 WC_Admin_Notices::add_custom_notice(
1612 $theme_slug . '_install_error',
1613 sprintf(
1614 // translators: 1: theme slug, 2: error message, 3: URL to install theme manually.
1615 __( '%1$s could not be installed (%2$s). <a href="%3$s">Please install it manually by clicking here.</a>', 'woocommerce' ),
1616 $theme_slug,
1617 $e->getMessage(),
1618 esc_url( admin_url( 'update.php?action=install-theme&theme=' . $theme_slug . '&_wpnonce=' . wp_create_nonce( 'install-theme_' . $theme_slug ) ) )
1619 )
1620 );
1621 }
1622
1623 // Discard feedback.
1624 ob_end_clean();
1625 }
1626 }
1627 }
1628
1629 WC_Install::init();
1630