Completed
Push — master ( 1a4576...dff1fc )
by Mike
10:40
created

WC_Install::init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 11
rs 9.4285
1
<?php
2
/**
3
 * Installation related functions and actions.
4
 *
5
 * @author   WooThemes
6
 * @category Admin
7
 * @package  WooCommerce/Classes
8
 * @version  2.4.1
9
 */
10
11
if ( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
/**
16
 * WC_Install Class.
17
 */
18
class WC_Install {
19
20
	/** @var array DB updates and callbacks that need to be run per version */
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_webhooks',
56
			'wc_update_240_refunds',
57
			'wc_update_240_db_version',
58
		),
59
		'2.4.1' => array(
60
			'wc_update_241_variations',
61
			'wc_update_241_db_version',
62
		),
63
		'2.5.0' => array(
64
			'wc_update_250_currency',
65
			'wc_update_250_db_version',
66
		),
67
		'2.6.0' => array(
68
			'wc_update_260_options',
69
			'wc_update_260_termmeta',
70
			'wc_update_260_zones',
71
			'wc_update_260_zone_methods',
72
			'wc_update_260_refunds',
73
			'wc_update_260_db_version',
74
		),
75
		'2.7.0' => array(
76
			'wc_update_270_webhooks',
77
		),
78
	);
79
80
	/** @var object Background update class */
81
	private static $background_updater;
82
83
	/**
84
	 * Hook in tabs.
85
	 */
86
	public static function init() {
87
		add_action( 'init', array( __CLASS__, 'check_version' ), 5 );
88
		add_action( 'init', array( __CLASS__, 'init_background_updater' ), 5 );
89
		add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
90
		add_action( 'in_plugin_update_message-woocommerce/woocommerce.php', array( __CLASS__, 'in_plugin_update_message' ) );
91
		add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
92
		add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
93
		add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
94
		add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
95
		add_action( 'woocommerce_plugin_background_installer', array( __CLASS__, 'background_installer' ), 10, 2 );
96
	}
97
98
	/**
99
	 * Init background updates
100
	 */
101
	public static function init_background_updater() {
102
		include_once( dirname( __FILE__ ) . '/class-wc-background-updater.php' );
103
		self::$background_updater = new WC_Background_Updater();
104
	}
105
106
	/**
107
	 * Check WooCommerce version and run the updater is required.
108
	 *
109
	 * This check is done on all requests and runs if he versions do not match.
110
	 */
111
	public static function check_version() {
112
		if ( ! defined( 'IFRAME_REQUEST' ) && get_option( 'woocommerce_version' ) !== WC()->version ) {
113
			self::install();
114
			do_action( 'woocommerce_updated' );
115
		}
116
	}
117
118
	/**
119
	 * Install actions when a update button is clicked within the admin area.
120
	 *
121
	 * This function is hooked into admin_init to affect admin only.
122
	 */
123
	public static function install_actions() {
124
		if ( ! empty( $_GET['do_update_woocommerce'] ) ) {
125
			self::update();
126
			WC_Admin_Notices::add_notice( 'update' );
127
		}
128
		if ( ! empty( $_GET['force_update_woocommerce'] ) ) {
129
			do_action( 'wp_wc_updater_cron' );
130
			wp_safe_redirect( admin_url( 'admin.php?page=wc-settings' ) );
131
		}
132
	}
133
134
	/**
135
	 * Install WC.
136
	 */
137
	public static function install() {
138
		global $wpdb;
139
140
		if ( ! defined( 'WC_INSTALLING' ) ) {
141
			define( 'WC_INSTALLING', true );
142
		}
143
144
		// Ensure needed classes are loaded
145
		include_once( dirname( __FILE__ ) . '/admin/class-wc-admin-notices.php' );
146
147
		self::create_options();
148
		self::create_tables();
149
		self::create_roles();
150
151
		// Register post types
152
		WC_Post_types::register_post_types();
153
		WC_Post_types::register_taxonomies();
154
155
		// Also register endpoints - this needs to be done prior to rewrite rule flush
156
		WC()->query->init_query_vars();
157
		WC()->query->add_endpoints();
158
		WC_API::add_endpoint();
159
		WC_Auth::add_endpoint();
160
161
		self::create_terms();
162
		self::create_cron_jobs();
163
		self::create_files();
164
165
		// Queue upgrades/setup wizard
166
		$current_wc_version    = get_option( 'woocommerce_version', null );
167
		$current_db_version    = get_option( 'woocommerce_db_version', null );
168
169
		WC_Admin_Notices::remove_all_notices();
170
171
		// No versions? This is a new install :)
172
		if ( is_null( $current_wc_version ) && is_null( $current_db_version ) && apply_filters( 'woocommerce_enable_setup_wizard', true ) ) {
173
			WC_Admin_Notices::add_notice( 'install' );
174
			set_transient( '_wc_activation_redirect', 1, 30 );
175
176
		// No page? Let user run wizard again..
177
		} elseif ( ! get_option( 'woocommerce_cart_page_id' ) ) {
178
			WC_Admin_Notices::add_notice( 'install' );
179
		}
180
181
		if ( ! is_null( $current_db_version ) && version_compare( $current_db_version, max( array_keys( self::$db_updates ) ), '<' ) ) {
182
			WC_Admin_Notices::add_notice( 'update' );
183
		} else {
184
			self::update_db_version();
185
		}
186
187
		self::update_wc_version();
188
189
		// Flush rules after install
190
		flush_rewrite_rules();
191
		delete_transient( 'wc_attribute_taxonomies' );
192
193
		/*
194
		 * Deletes all expired transients. The multi-table delete syntax is used
195
		 * to delete the transient record from table a, and the corresponding
196
		 * transient_timeout record from table b.
197
		 *
198
		 * Based on code inside core's upgrade_network() function.
199
		 */
200
		$sql = "DELETE a, b FROM $wpdb->options a, $wpdb->options b
201
			WHERE a.option_name LIKE %s
202
			AND a.option_name NOT LIKE %s
203
			AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
204
			AND b.option_value < %d";
205
		$wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_transient_' ) . '%', $wpdb->esc_like( '_transient_timeout_' ) . '%', time() ) );
206
207
		// Trigger action
208
		do_action( 'woocommerce_installed' );
209
	}
210
211
	/**
212
	 * Update WC version to current.
213
	 */
214
	private static function update_wc_version() {
215
		delete_option( 'woocommerce_version' );
216
		add_option( 'woocommerce_version', WC()->version );
217
	}
218
219
	/**
220
	 * Push all needed DB updates to the queue for processing.
221
	 */
222
	private static function update() {
223
		$current_db_version = get_option( 'woocommerce_db_version' );
224
		$logger             = wc_get_logger();
225
		$update_queued      = false;
226
227
		foreach ( self::$db_updates as $version => $update_callbacks ) {
228
			if ( version_compare( $current_db_version, $version, '<' ) ) {
229
				foreach ( $update_callbacks as $update_callback ) {
230
					$logger->add( 'wc_db_updates', sprintf( 'Queuing %s - %s', $version, $update_callback ) );
231
					self::$background_updater->push_to_queue( $update_callback );
232
					$update_queued = true;
233
				}
234
			}
235
		}
236
237
		if ( $update_queued ) {
238
			self::$background_updater->save()->dispatch();
239
		}
240
	}
241
242
	/**
243
	 * Update DB version to current.
244
	 * @param string $version
245
	 */
246
	public static function update_db_version( $version = null ) {
247
		delete_option( 'woocommerce_db_version' );
248
		add_option( 'woocommerce_db_version', is_null( $version ) ? WC()->version : $version );
249
	}
250
251
	/**
252
	 * Add more cron schedules.
253
	 * @param  array $schedules
254
	 * @return array
255
	 */
256
	public static function cron_schedules( $schedules ) {
257
		$schedules['monthly'] = array(
258
			'interval' => 2635200,
259
			'display'  => __( 'Monthly', 'woocommerce' )
260
		);
261
		return $schedules;
262
	}
263
264
	/**
265
	 * Create cron jobs (clear them first).
266
	 */
267
	private static function create_cron_jobs() {
268
		wp_clear_scheduled_hook( 'woocommerce_scheduled_sales' );
269
		wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
270
		wp_clear_scheduled_hook( 'woocommerce_cleanup_sessions' );
271
		wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
272
		wp_clear_scheduled_hook( 'woocommerce_tracker_send_event' );
273
274
		$ve = get_option( 'gmt_offset' ) > 0 ? '+' : '-';
275
276
		wp_schedule_event( strtotime( '00:00 tomorrow ' . $ve . get_option( 'gmt_offset' ) . ' HOURS' ), 'daily', 'woocommerce_scheduled_sales' );
277
278
		$held_duration = get_option( 'woocommerce_hold_stock_minutes', '60' );
279
280
		if ( $held_duration != '' ) {
281
			wp_schedule_single_event( time() + ( absint( $held_duration ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
282
		}
283
284
		wp_schedule_event( time(), 'twicedaily', 'woocommerce_cleanup_sessions' );
285
		wp_schedule_event( strtotime( 'first tuesday of next month' ), 'monthly', 'woocommerce_geoip_updater' );
286
		wp_schedule_event( time(), apply_filters( 'woocommerce_tracker_event_recurrence', 'daily' ), 'woocommerce_tracker_send_event' );
287
	}
288
289
	/**
290
	 * Create pages that the plugin relies on, storing page id's in variables.
291
	 */
292
	public static function create_pages() {
293
		include_once( dirname( __FILE__ ) . '/admin/wc-admin-functions.php' );
294
295
		$pages = apply_filters( 'woocommerce_create_pages', array(
296
			'shop' => array(
297
				'name'    => _x( 'shop', 'Page slug', 'woocommerce' ),
298
				'title'   => _x( 'Shop', 'Page title', 'woocommerce' ),
299
				'content' => ''
300
			),
301
			'cart' => array(
302
				'name'    => _x( 'cart', 'Page slug', 'woocommerce' ),
303
				'title'   => _x( 'Cart', 'Page title', 'woocommerce' ),
304
				'content' => '[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']'
305
			),
306
			'checkout' => array(
307
				'name'    => _x( 'checkout', 'Page slug', 'woocommerce' ),
308
				'title'   => _x( 'Checkout', 'Page title', 'woocommerce' ),
309
				'content' => '[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']'
310
			),
311
			'myaccount' => array(
312
				'name'    => _x( 'my-account', 'Page slug', 'woocommerce' ),
313
				'title'   => _x( 'My Account', 'Page title', 'woocommerce' ),
314
				'content' => '[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']'
315
			)
316
		) );
317
318
		foreach ( $pages as $key => $page ) {
319
			wc_create_page( esc_sql( $page['name'] ), 'woocommerce_' . $key . '_page_id', $page['title'], $page['content'], ! empty( $page['parent'] ) ? wc_get_page_id( $page['parent'] ) : '' );
320
		}
321
322
		delete_transient( 'woocommerce_cache_excluded_uris' );
323
	}
324
325
	/**
326
	 * Default options.
327
	 *
328
	 * Sets up the default options used on the settings page.
329
	 */
330
	private static function create_options() {
331
		// Include settings so that we can run through defaults
332
		include_once( dirname( __FILE__ ) . '/admin/class-wc-admin-settings.php' );
333
334
		$settings = WC_Admin_Settings::get_settings_pages();
335
336
		foreach ( $settings as $section ) {
337
			if ( ! method_exists( $section, 'get_settings' ) ) {
338
				continue;
339
			}
340
			$subsections = array_unique( array_merge( array( '' ), array_keys( $section->get_sections() ) ) );
341
342
			foreach ( $subsections as $subsection ) {
343
				foreach ( $section->get_settings( $subsection ) as $value ) {
344
					if ( isset( $value['default'] ) && isset( $value['id'] ) ) {
345
						$autoload = isset( $value['autoload'] ) ? (bool) $value['autoload'] : true;
346
						add_option( $value['id'], $value['default'], '', ( $autoload ? 'yes' : 'no' ) );
347
					}
348
				}
349
			}
350
		}
351
	}
352
353
	/**
354
	 * Add the default terms for WC taxonomies - product types and order statuses. Modify this at your own risk.
355
	 */
356
	private static function create_terms() {
357
		$taxonomies = array(
358
			'product_type' => array(
359
				'simple',
360
				'grouped',
361
				'variable',
362
				'external'
363
			)
364
		);
365
366
		foreach ( $taxonomies as $taxonomy => $terms ) {
367
			foreach ( $terms as $term ) {
368
				if ( ! get_term_by( 'slug', sanitize_title( $term ), $taxonomy ) ) {
369
					wp_insert_term( $term, $taxonomy );
370
				}
371
			}
372
		}
373
	}
374
375
	/**
376
	 * Set up the database tables which the plugin needs to function.
377
	 *
378
	 * Tables:
379
	 *		woocommerce_attribute_taxonomies - Table for storing attribute taxonomies - these are user defined
380
	 *		woocommerce_termmeta - Term meta table - sadly WordPress does not have termmeta so we need our own
381
	 *		woocommerce_downloadable_product_permissions - Table for storing user and guest download permissions.
382
	 *			KEY(order_id, product_id, download_id) used for organizing downloads on the My Account page
383
	 *		woocommerce_order_items - Order line items are stored in a table to make them easily queryable for reports
384
	 *		woocommerce_order_itemmeta - Order line item meta is stored in a table for storing extra data.
385
	 *		woocommerce_tax_rates - Tax Rates are stored inside 2 tables making tax queries simple and efficient.
386
	 *		woocommerce_tax_rate_locations - Each rate can be applied to more than one postcode/city hence the second table.
387
	 */
388
	private static function create_tables() {
389
		global $wpdb;
390
391
		$wpdb->hide_errors();
392
393
		require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
394
395
		/**
396
		 * Before updating with DBDELTA, remove any primary keys which could be
397
		 * modified due to schema updates.
398
		 */
399 View Code Duplication
		if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}woocommerce_downloadable_product_permissions';" ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400
			if ( ! $wpdb->get_var( "SHOW COLUMNS FROM `{$wpdb->prefix}woocommerce_downloadable_product_permissions` LIKE 'permission_id';" ) ) {
401
				$wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions DROP PRIMARY KEY, ADD `permission_id` bigint(20) NOT NULL PRIMARY KEY AUTO_INCREMENT;" );
402
			}
403
		}
404
405
		dbDelta( self::get_schema() );
406
407
		$index_exists = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->comments} WHERE Column_name = 'comment_type' and Key_name = 'comment_type'" );
408
409
		if ( is_null( $index_exists ) ) {
410
			// Add an index to the field comment_type to improve the response time of the query
411
			// used by WC_Comments::wp_count_comments() to get the number of comments by type.
412
			$wpdb->query( "ALTER TABLE {$wpdb->comments} ADD INDEX woo_idx_comment_type (comment_type)" );
413
		}
414
	}
415
416
	/**
417
	 * Get Table schema.
418
	 * https://github.com/woothemes/woocommerce/wiki/Database-Description/
419
	 * @return string
420
	 */
421
	private static function get_schema() {
422
		global $wpdb;
423
424
		$collate = '';
425
426
		if ( $wpdb->has_cap( 'collation' ) ) {
427
			$collate = $wpdb->get_charset_collate();
428
		}
429
430
		/*
431
		 * Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
432
		 * As of WordPress 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which
433
		 * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
434
		 *
435
		 * This may cause duplicate index notices in logs due to https://core.trac.wordpress.org/ticket/34870 but dropping
436
		 * indexes first causes too much load on some servers/larger DB.
437
		 */
438
		$max_index_length = 191;
439
440
		$tables = "
441
CREATE TABLE {$wpdb->prefix}woocommerce_sessions (
442
  session_id bigint(20) NOT NULL AUTO_INCREMENT,
443
  session_key char(32) NOT NULL,
444
  session_value longtext NOT NULL,
445
  session_expiry bigint(20) NOT NULL,
446
  UNIQUE KEY session_id (session_id),
447
  PRIMARY KEY  (session_key)
448
) $collate;
449
CREATE TABLE {$wpdb->prefix}woocommerce_api_keys (
450
  key_id bigint(20) NOT NULL auto_increment,
451
  user_id bigint(20) NOT NULL,
452
  description longtext NULL,
453
  permissions varchar(10) NOT NULL,
454
  consumer_key char(64) NOT NULL,
455
  consumer_secret char(43) NOT NULL,
456
  nonces longtext NULL,
457
  truncated_key char(7) NOT NULL,
458
  last_access datetime NULL default null,
459
  PRIMARY KEY  (key_id),
460
  KEY consumer_key (consumer_key),
461
  KEY consumer_secret (consumer_secret)
462
) $collate;
463
CREATE TABLE {$wpdb->prefix}woocommerce_attribute_taxonomies (
464
  attribute_id bigint(20) NOT NULL auto_increment,
465
  attribute_name varchar(200) NOT NULL,
466
  attribute_label longtext NULL,
467
  attribute_type varchar(200) NOT NULL,
468
  attribute_orderby varchar(200) NOT NULL,
469
  attribute_public int(1) NOT NULL DEFAULT 1,
470
  PRIMARY KEY  (attribute_id),
471
  KEY attribute_name (attribute_name($max_index_length))
472
) $collate;
473
CREATE TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions (
474
  permission_id bigint(20) NOT NULL auto_increment,
475
  download_id varchar(32) NOT NULL,
476
  product_id bigint(20) NOT NULL,
477
  order_id bigint(20) NOT NULL DEFAULT 0,
478
  order_key varchar(200) NOT NULL,
479
  user_email varchar(200) NOT NULL,
480
  user_id bigint(20) NULL,
481
  downloads_remaining varchar(9) NULL,
482
  access_granted datetime NOT NULL default '0000-00-00 00:00:00',
483
  access_expires datetime NULL default null,
484
  download_count bigint(20) NOT NULL DEFAULT 0,
485
  PRIMARY KEY  (permission_id),
486
  KEY download_order_key_product (product_id,order_id,order_key($max_index_length),download_id),
487
  KEY download_order_product (download_id,order_id,product_id)
488
) $collate;
489
CREATE TABLE {$wpdb->prefix}woocommerce_order_items (
490
  order_item_id bigint(20) NOT NULL auto_increment,
491
  order_item_name longtext NOT NULL,
492
  order_item_type varchar(200) NOT NULL DEFAULT '',
493
  order_id bigint(20) NOT NULL,
494
  PRIMARY KEY  (order_item_id),
495
  KEY order_id (order_id)
496
) $collate;
497
CREATE TABLE {$wpdb->prefix}woocommerce_order_itemmeta (
498
  meta_id bigint(20) NOT NULL auto_increment,
499
  order_item_id bigint(20) NOT NULL,
500
  meta_key varchar(255) default NULL,
501
  meta_value longtext NULL,
502
  PRIMARY KEY  (meta_id),
503
  KEY order_item_id (order_item_id),
504
  KEY meta_key (meta_key($max_index_length))
505
) $collate;
506
CREATE TABLE {$wpdb->prefix}woocommerce_tax_rates (
507
  tax_rate_id bigint(20) NOT NULL auto_increment,
508
  tax_rate_country varchar(200) NOT NULL DEFAULT '',
509
  tax_rate_state varchar(200) NOT NULL DEFAULT '',
510
  tax_rate varchar(200) NOT NULL DEFAULT '',
511
  tax_rate_name varchar(200) NOT NULL DEFAULT '',
512
  tax_rate_priority bigint(20) NOT NULL,
513
  tax_rate_compound int(1) NOT NULL DEFAULT 0,
514
  tax_rate_shipping int(1) NOT NULL DEFAULT 1,
515
  tax_rate_order bigint(20) NOT NULL,
516
  tax_rate_class varchar(200) NOT NULL DEFAULT '',
517
  PRIMARY KEY  (tax_rate_id),
518
  KEY tax_rate_country (tax_rate_country($max_index_length)),
519
  KEY tax_rate_state (tax_rate_state($max_index_length)),
520
  KEY tax_rate_class (tax_rate_class($max_index_length)),
521
  KEY tax_rate_priority (tax_rate_priority)
522
) $collate;
523
CREATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations (
524
  location_id bigint(20) NOT NULL auto_increment,
525
  location_code varchar(255) NOT NULL,
526
  tax_rate_id bigint(20) NOT NULL,
527
  location_type varchar(40) NOT NULL,
528
  PRIMARY KEY  (location_id),
529
  KEY tax_rate_id (tax_rate_id),
530
  KEY location_type (location_type),
531
  KEY location_type_code (location_type(40),location_code(90))
532
) $collate;
533
CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zones (
534
  zone_id bigint(20) NOT NULL auto_increment,
535
  zone_name varchar(255) NOT NULL,
536
  zone_order bigint(20) NOT NULL,
537
  PRIMARY KEY  (zone_id)
538
) $collate;
539
CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_locations (
540
  location_id bigint(20) NOT NULL auto_increment,
541
  zone_id bigint(20) NOT NULL,
542
  location_code varchar(255) NOT NULL,
543
  location_type varchar(40) NOT NULL,
544
  PRIMARY KEY  (location_id),
545
  KEY location_id (location_id),
546
  KEY location_type (location_type),
547
  KEY location_type_code (location_type(40),location_code(90))
548
) $collate;
549
CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_methods (
550
  zone_id bigint(20) NOT NULL,
551
  instance_id bigint(20) NOT NULL auto_increment,
552
  method_id varchar(255) NOT NULL,
553
  method_order bigint(20) NOT NULL,
554
  is_enabled tinyint(1) NOT NULL DEFAULT '1',
555
  PRIMARY KEY  (instance_id)
556
) $collate;
557
CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokens (
558
  token_id bigint(20) NOT NULL auto_increment,
559
  gateway_id varchar(255) NOT NULL,
560
  token text NOT NULL,
561
  user_id bigint(20) NOT NULL DEFAULT '0',
562
  type varchar(255) NOT NULL,
563
  is_default tinyint(1) NOT NULL DEFAULT '0',
564
  PRIMARY KEY  (token_id),
565
  KEY user_id (user_id)
566
) $collate;
567
CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokenmeta (
568
  meta_id bigint(20) NOT NULL auto_increment,
569
  payment_token_id bigint(20) NOT NULL,
570
  meta_key varchar(255) NULL,
571
  meta_value longtext NULL,
572
  PRIMARY KEY  (meta_id),
573
  KEY payment_token_id (payment_token_id),
574
  KEY meta_key (meta_key($max_index_length))
575
) $collate;
576
		";
577
578
		// Term meta is only needed for old installs.
579
		if ( ! function_exists( 'get_term_meta' ) ) {
580
			$tables .= "
581
CREATE TABLE {$wpdb->prefix}woocommerce_termmeta (
582
  meta_id bigint(20) NOT NULL auto_increment,
583
  woocommerce_term_id bigint(20) NOT NULL,
584
  meta_key varchar(255) default NULL,
585
  meta_value longtext NULL,
586
  PRIMARY KEY  (meta_id),
587
  KEY woocommerce_term_id (woocommerce_term_id),
588
  KEY meta_key (meta_key($max_index_length))
589
) $collate;
590
			";
591
		}
592
593
		return $tables;
594
	}
595
596
	/**
597
	 * Create roles and capabilities.
598
	 */
599
	public static function create_roles() {
600
		global $wp_roles;
601
602
		if ( ! class_exists( 'WP_Roles' ) ) {
603
			return;
604
		}
605
606
		if ( ! isset( $wp_roles ) ) {
607
			$wp_roles = new WP_Roles();
608
		}
609
610
		// Customer role
611
		add_role( 'customer', __( 'Customer', 'woocommerce' ), array(
612
			'read' 					=> true
613
		) );
614
615
		// Shop manager role
616
		add_role( 'shop_manager', __( 'Shop Manager', 'woocommerce' ), array(
617
			'level_9'                => true,
618
			'level_8'                => true,
619
			'level_7'                => true,
620
			'level_6'                => true,
621
			'level_5'                => true,
622
			'level_4'                => true,
623
			'level_3'                => true,
624
			'level_2'                => true,
625
			'level_1'                => true,
626
			'level_0'                => true,
627
			'read'                   => true,
628
			'read_private_pages'     => true,
629
			'read_private_posts'     => true,
630
			'edit_users'             => true,
631
			'edit_posts'             => true,
632
			'edit_pages'             => true,
633
			'edit_published_posts'   => true,
634
			'edit_published_pages'   => true,
635
			'edit_private_pages'     => true,
636
			'edit_private_posts'     => true,
637
			'edit_others_posts'      => true,
638
			'edit_others_pages'      => true,
639
			'publish_posts'          => true,
640
			'publish_pages'          => true,
641
			'delete_posts'           => true,
642
			'delete_pages'           => true,
643
			'delete_private_pages'   => true,
644
			'delete_private_posts'   => true,
645
			'delete_published_pages' => true,
646
			'delete_published_posts' => true,
647
			'delete_others_posts'    => true,
648
			'delete_others_pages'    => true,
649
			'manage_categories'      => true,
650
			'manage_links'           => true,
651
			'moderate_comments'      => true,
652
			'unfiltered_html'        => true,
653
			'upload_files'           => true,
654
			'export'                 => true,
655
			'import'                 => true,
656
			'list_users'             => true
657
		) );
658
659
		$capabilities = self::get_core_capabilities();
660
661
		foreach ( $capabilities as $cap_group ) {
662
			foreach ( $cap_group as $cap ) {
663
				$wp_roles->add_cap( 'shop_manager', $cap );
664
				$wp_roles->add_cap( 'administrator', $cap );
665
			}
666
		}
667
	}
668
669
	/**
670
	 * Get capabilities for WooCommerce - these are assigned to admin/shop manager during installation or reset.
671
	 *
672
	 * @return array
673
	 */
674
	 private static function get_core_capabilities() {
675
		$capabilities = array();
676
677
		$capabilities['core'] = array(
678
			'manage_woocommerce',
679
			'view_woocommerce_reports'
680
		);
681
682
		$capability_types = array( 'product', 'shop_order', 'shop_coupon', 'shop_webhook' );
683
684
		foreach ( $capability_types as $capability_type ) {
685
686
			$capabilities[ $capability_type ] = array(
687
				// Post type
688
				"edit_{$capability_type}",
689
				"read_{$capability_type}",
690
				"delete_{$capability_type}",
691
				"edit_{$capability_type}s",
692
				"edit_others_{$capability_type}s",
693
				"publish_{$capability_type}s",
694
				"read_private_{$capability_type}s",
695
				"delete_{$capability_type}s",
696
				"delete_private_{$capability_type}s",
697
				"delete_published_{$capability_type}s",
698
				"delete_others_{$capability_type}s",
699
				"edit_private_{$capability_type}s",
700
				"edit_published_{$capability_type}s",
701
702
				// Terms
703
				"manage_{$capability_type}_terms",
704
				"edit_{$capability_type}_terms",
705
				"delete_{$capability_type}_terms",
706
				"assign_{$capability_type}_terms"
707
			);
708
		}
709
710
		return $capabilities;
711
	}
712
713
	/**
714
	 * woocommerce_remove_roles function.
715
	 */
716
	public static function remove_roles() {
717
		global $wp_roles;
718
719
		if ( ! class_exists( 'WP_Roles' ) ) {
720
			return;
721
		}
722
723
		if ( ! isset( $wp_roles ) ) {
724
			$wp_roles = new WP_Roles();
725
		}
726
727
		$capabilities = self::get_core_capabilities();
728
729
		foreach ( $capabilities as $cap_group ) {
730
			foreach ( $cap_group as $cap ) {
731
				$wp_roles->remove_cap( 'shop_manager', $cap );
732
				$wp_roles->remove_cap( 'administrator', $cap );
733
			}
734
		}
735
736
		remove_role( 'customer' );
737
		remove_role( 'shop_manager' );
738
	}
739
740
	/**
741
	 * Create files/directories.
742
	 */
743
	private static function create_files() {
744
		// Install files and folders for uploading files and prevent hotlinking
745
		$upload_dir      = wp_upload_dir();
746
		$download_method = get_option( 'woocommerce_file_download_method', 'force' );
747
748
		$files = array(
749
			array(
750
				'base' 		=> $upload_dir['basedir'] . '/woocommerce_uploads',
751
				'file' 		=> 'index.html',
752
				'content' 	=> ''
753
			),
754
			array(
755
				'base' 		=> WC_LOG_DIR,
756
				'file' 		=> '.htaccess',
757
				'content' 	=> 'deny from all'
758
			),
759
			array(
760
				'base' 		=> WC_LOG_DIR,
761
				'file' 		=> 'index.html',
762
				'content' 	=> ''
763
			)
764
		);
765
766
		if ( 'redirect' !== $download_method ) {
767
			$files[] = array(
768
				'base' 		=> $upload_dir['basedir'] . '/woocommerce_uploads',
769
				'file' 		=> '.htaccess',
770
				'content' 	=> 'deny from all'
771
			);
772
		}
773
774
		foreach ( $files as $file ) {
775
			if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
776
				if ( $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'w' ) ) {
777
					fwrite( $file_handle, $file['content'] );
778
					fclose( $file_handle );
779
				}
780
			}
781
		}
782
	}
783
784
	/**
785
	 * Show plugin changes. Code adapted from W3 Total Cache.
786
	 */
787
	public static function in_plugin_update_message( $args ) {
788
		$transient_name = 'wc_upgrade_notice_' . $args['Version'];
789
790
		if ( false === ( $upgrade_notice = get_transient( $transient_name ) ) ) {
791
			$response = wp_safe_remote_get( 'https://plugins.svn.wordpress.org/woocommerce/trunk/readme.txt' );
792
793
			if ( ! is_wp_error( $response ) && ! empty( $response['body'] ) ) {
794
				$upgrade_notice = self::parse_update_notice( $response['body'], $args['new_version'] );
795
				set_transient( $transient_name, $upgrade_notice, DAY_IN_SECONDS );
796
			}
797
		}
798
799
		echo wp_kses_post( $upgrade_notice );
800
	}
801
802
	/**
803
	 * Parse update notice from readme file.
804
	 *
805
	 * @param  string $content
806
	 * @param  string $new_version
807
	 * @return string
808
	 */
809
	private static function parse_update_notice( $content, $new_version ) {
810
		// Output Upgrade Notice.
811
		$matches        = null;
812
		$regexp         = '~==\s*Upgrade Notice\s*==\s*=\s*(.*)\s*=(.*)(=\s*' . preg_quote( WC_VERSION ) . '\s*=|$)~Uis';
813
		$upgrade_notice = '';
814
815
		if ( preg_match( $regexp, $content, $matches ) ) {
816
			$version = trim( $matches[1] );
817
			$notices = (array) preg_split('~[\r\n]+~', trim( $matches[2] ) );
818
819
			// Check the latest stable version and ignore trunk.
820
			if ( $version === $new_version && version_compare( WC_VERSION, $version, '<' ) ) {
821
822
				$upgrade_notice .= '<div class="wc_plugin_upgrade_notice">';
823
824
				foreach ( $notices as $index => $line ) {
825
					$upgrade_notice .= wp_kses_post( preg_replace( '~\[([^\]]*)\]\(([^\)]*)\)~', '<a href="${2}">${1}</a>', $line ) );
826
				}
827
828
				$upgrade_notice .= '</div> ';
829
			}
830
		}
831
832
		return wp_kses_post( $upgrade_notice );
833
	}
834
835
	/**
836
	 * Show action links on the plugin screen.
837
	 *
838
	 * @param	mixed $links Plugin Action links
839
	 * @return	array
840
	 */
841
	public static function plugin_action_links( $links ) {
842
		$action_links = array(
843
			'settings' => '<a href="' . admin_url( 'admin.php?page=wc-settings' ) . '" title="' . esc_attr( __( 'View WooCommerce Settings', 'woocommerce' ) ) . '">' . __( 'Settings', 'woocommerce' ) . '</a>',
844
		);
845
846
		return array_merge( $action_links, $links );
847
	}
848
849
	/**
850
	 * Show row meta on the plugin screen.
851
	 *
852
	 * @param	mixed $links Plugin Row Meta
853
	 * @param	mixed $file  Plugin Base file
854
	 * @return	array
855
	 */
856
	public static function plugin_row_meta( $links, $file ) {
857
		if ( $file == WC_PLUGIN_BASENAME ) {
858
			$row_meta = array(
859
				'docs'    => '<a href="' . esc_url( apply_filters( 'woocommerce_docs_url', 'https://docs.woocommerce.com/documentation/plugins/woocommerce/' ) ) . '" title="' . esc_attr( __( 'View WooCommerce Documentation', 'woocommerce' ) ) . '">' . __( 'Docs', 'woocommerce' ) . '</a>',
860
				'apidocs' => '<a href="' . esc_url( apply_filters( 'woocommerce_apidocs_url', 'https://docs.woocommerce.com/wc-apidocs/' ) ) . '" title="' . esc_attr( __( 'View WooCommerce API Docs', 'woocommerce' ) ) . '">' . __( 'API Docs', 'woocommerce' ) . '</a>',
861
				'support' => '<a href="' . esc_url( apply_filters( 'woocommerce_support_url', 'https://support.woothemes.com/' ) ) . '" title="' . esc_attr( __( 'Visit Premium Customer Support Forum', 'woocommerce' ) ) . '">' . __( 'Premium Support', 'woocommerce' ) . '</a>',
862
			);
863
864
			return array_merge( $links, $row_meta );
865
		}
866
867
		return (array) $links;
868
	}
869
870
	/**
871
	 * Uninstall tables when MU blog is deleted.
872
	 * @param  array $tables
873
	 * @return string[]
874
	 */
875
	public static function wpmu_drop_tables( $tables ) {
876
		global $wpdb;
877
878
		$tables[] = $wpdb->prefix . 'woocommerce_sessions';
879
		$tables[] = $wpdb->prefix . 'woocommerce_api_keys';
880
		$tables[] = $wpdb->prefix . 'woocommerce_attribute_taxonomies';
881
		$tables[] = $wpdb->prefix . 'woocommerce_downloadable_product_permissions';
882
		$tables[] = $wpdb->prefix . 'woocommerce_termmeta';
883
		$tables[] = $wpdb->prefix . 'woocommerce_tax_rates';
884
		$tables[] = $wpdb->prefix . 'woocommerce_tax_rate_locations';
885
		$tables[] = $wpdb->prefix . 'woocommerce_order_items';
886
		$tables[] = $wpdb->prefix . 'woocommerce_order_itemmeta';
887
888
		return $tables;
889
	}
890
891
	/**
892
	 * Get slug from path
893
	 * @param  string $key
894
	 * @return string
895
	 */
896
	private static function format_plugin_slug( $key ) {
897
		$slug = explode( '/', $key );
898
		$slug = explode( '.', end( $slug ) );
899
		return $slug[0];
900
	}
901
902
	/**
903
	 * Install a plugin from .org in the background via a cron job (used by
904
	 * installer - opt in).
905
	 * @param string $plugin_to_install_id
906
	 * @param array $plugin_to_install
907
	 * @since 2.6.0
908
	 */
909
	public static function background_installer( $plugin_to_install_id, $plugin_to_install ) {
910
		if ( ! empty( $plugin_to_install['repo-slug'] ) ) {
911
			require_once( ABSPATH . 'wp-admin/includes/file.php' );
912
			require_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
913
			require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
914
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
915
916
			WP_Filesystem();
917
918
			$skin              = new Automatic_Upgrader_Skin;
919
			$upgrader          = new WP_Upgrader( $skin );
920
			$installed_plugins = array_map( array( __CLASS__, 'format_plugin_slug' ), array_keys( get_plugins() ) );
921
			$plugin_slug       = $plugin_to_install['repo-slug'];
922
			$plugin            = $plugin_slug . '/' . $plugin_slug . '.php';
923
			$installed         = false;
924
			$activate          = false;
925
926
			// See if the plugin is installed already
927
			if ( in_array( $plugin_to_install['repo-slug'], $installed_plugins ) ) {
928
				$installed = true;
929
				$activate  = ! is_plugin_active( $plugin );
930
			}
931
932
			// Install this thing!
933
			if ( ! $installed ) {
934
				// Suppress feedback
935
				ob_start();
936
937
				try {
938
					$plugin_information = plugins_api( 'plugin_information', array(
939
						'slug'   => $plugin_to_install['repo-slug'],
940
						'fields' => array(
941
							'short_description' => false,
942
							'sections'          => false,
943
							'requires'          => false,
944
							'rating'            => false,
945
							'ratings'           => false,
946
							'downloaded'        => false,
947
							'last_updated'      => false,
948
							'added'             => false,
949
							'tags'              => false,
950
							'homepage'          => false,
951
							'donate_link'       => false,
952
							'author_profile'    => false,
953
							'author'            => false,
954
						),
955
					) );
956
957
					if ( is_wp_error( $plugin_information ) ) {
958
						throw new Exception( $plugin_information->get_error_message() );
959
					}
960
961
					$package  = $plugin_information->download_link;
962
					$download = $upgrader->download_package( $package );
963
964
					if ( is_wp_error( $download ) ) {
965
						throw new Exception( $download->get_error_message() );
966
					}
967
968
					$working_dir = $upgrader->unpack_package( $download, true );
969
970
					if ( is_wp_error( $working_dir ) ) {
971
						throw new Exception( $working_dir->get_error_message() );
972
					}
973
974
					$result = $upgrader->install_package( array(
975
						'source'                      => $working_dir,
976
						'destination'                 => WP_PLUGIN_DIR,
977
						'clear_destination'           => false,
978
						'abort_if_destination_exists' => false,
979
						'clear_working'               => true,
980
						'hook_extra'                  => array(
981
							'type'   => 'plugin',
982
							'action' => 'install',
983
						),
984
					) );
985
986
					if ( is_wp_error( $result ) ) {
987
						throw new Exception( $result->get_error_message() );
988
					}
989
990
					$activate = true;
991
992
				} catch ( Exception $e ) {
993
					WC_Admin_Notices::add_custom_notice(
994
						$plugin_to_install_id . '_install_error',
995
						sprintf(
996
							__( '%1$s could not be installed (%2$s). %3$sPlease install it manually by clicking here.%4$s', 'woocommerce' ),
997
							$plugin_to_install['name'],
998
							$e->getMessage(),
999
							'<a href="' . esc_url( admin_url( 'index.php?wc-install-plugin-redirect=' . $plugin_to_install['repo-slug'] ) ) . '">',
1000
							'</a>'
1001
						)
1002
					);
1003
				}
1004
1005
				// Discard feedback
1006
				ob_end_clean();
1007
			}
1008
1009
			wp_clean_plugins_cache();
1010
1011
			// Activate this thing
1012
			if ( $activate ) {
1013
				try {
1014
					$result = activate_plugin( $plugin );
1015
1016
					if ( is_wp_error( $result ) ) {
1017
						throw new Exception( $result->get_error_message() );
1018
					}
1019
1020
				} catch ( Exception $e ) {
1021
					WC_Admin_Notices::add_custom_notice(
1022
						$plugin_to_install_id . '_install_error',
1023
						sprintf(
1024
							__( '%1$s was installed but could not be activated. %2$sPlease activate it manually by clicking here.%3$s', 'woocommerce' ),
1025
							$plugin_to_install['name'],
1026
							'<a href="' . admin_url( 'plugins.php' ) . '">',
1027
							'</a>'
1028
						)
1029
					);
1030
				}
1031
			}
1032
		}
1033
	}
1034
}
1035
1036
WC_Install::init();
1037