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