WC_Install::get_core_capabilities()   B
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 38
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 38
rs 8.8571
c 0
b 0
f 0
eloc 26
nc 2
nop 0
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
	);
76
77
	/** @var object Background update class */
78
	private static $background_updater;
79
80
	/**
81
	 * Hook in tabs.
82
	 */
83
	public static function init() {
84
		add_action( 'init', array( __CLASS__, 'check_version' ), 5 );
85
		add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
86
		add_action( 'in_plugin_update_message-woocommerce/woocommerce.php', array( __CLASS__, 'in_plugin_update_message' ) );
87
		add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
88
		add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
89
		add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
90
		add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
91
		add_action( 'woocommerce_plugin_background_installer', array( __CLASS__, 'background_installer' ), 10, 2 );
92
93
		// Init background updates
94
		self::$background_updater = new WC_Background_Updater();
95
	}
96
97
	/**
98
	 * Check WooCommerce version and run the updater is required.
99
	 *
100
	 * This check is done on all requests and runs if he versions do not match.
101
	 */
102
	public static function check_version() {
103
		if ( ! defined( 'IFRAME_REQUEST' ) && get_option( 'woocommerce_version' ) !== WC()->version ) {
104
			self::install();
105
			do_action( 'woocommerce_updated' );
106
		}
107
	}
108
109
	/**
110
	 * Install actions when a update button is clicked within the admin area.
111
	 *
112
	 * This function is hooked into admin_init to affect admin only.
113
	 */
114
	public static function install_actions() {
115
		if ( ! empty( $_GET['do_update_woocommerce'] ) ) {
116
			self::update();
117
			WC_Admin_Notices::remove_notice( 'update' );
118
		}
119
	}
120
121
	/**
122
	 * Install WC.
123
	 */
124
	public static function install() {
125
		global $wpdb;
126
127
		if ( ! defined( 'WC_INSTALLING' ) ) {
128
			define( 'WC_INSTALLING', true );
129
		}
130
131
		// Ensure needed classes are loaded
132
		include_once( 'admin/class-wc-admin-notices.php' );
133
134
		self::create_options();
135
		self::create_tables();
136
		self::create_roles();
137
138
		// Register post types
139
		WC_Post_types::register_post_types();
140
		WC_Post_types::register_taxonomies();
141
142
		// Also register endpoints - this needs to be done prior to rewrite rule flush
143
		WC()->query->init_query_vars();
144
		WC()->query->add_endpoints();
145
		WC_API::add_endpoint();
146
		WC_Auth::add_endpoint();
147
148
		self::create_terms();
149
		self::create_cron_jobs();
150
		self::create_files();
151
152
		// Queue upgrades/setup wizard
153
		$current_wc_version    = get_option( 'woocommerce_version', null );
154
		$current_db_version    = get_option( 'woocommerce_db_version', null );
155
156
		WC_Admin_Notices::remove_all_notices();
157
158
		// No versions? This is a new install :)
159
		if ( is_null( $current_wc_version ) && is_null( $current_db_version ) && apply_filters( 'woocommerce_enable_setup_wizard', true ) ) {
160
			WC_Admin_Notices::add_notice( 'install' );
161
			set_transient( '_wc_activation_redirect', 1, 30 );
162
163
		// No page? Let user run wizard again..
164
		} elseif ( ! get_option( 'woocommerce_cart_page_id' ) ) {
165
			WC_Admin_Notices::add_notice( 'install' );
166
		}
167
168
		if ( ! is_null( $current_db_version ) && version_compare( $current_db_version, max( array_keys( self::$db_updates ) ), '<' ) ) {
169
			WC_Admin_Notices::add_notice( 'update' );
170
		} else {
171
			self::update_db_version();
172
		}
173
174
		self::update_wc_version();
175
176
		// Flush rules after install
177
		flush_rewrite_rules();
178
		delete_transient( 'wc_attribute_taxonomies' );
179
180
		/*
181
		 * Deletes all expired transients. The multi-table delete syntax is used
182
		 * to delete the transient record from table a, and the corresponding
183
		 * transient_timeout record from table b.
184
		 *
185
		 * Based on code inside core's upgrade_network() function.
186
		 */
187
		$sql = "DELETE a, b FROM $wpdb->options a, $wpdb->options b
188
			WHERE a.option_name LIKE %s
189
			AND a.option_name NOT LIKE %s
190
			AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
191
			AND b.option_value < %d";
192
		$wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_transient_' ) . '%', $wpdb->esc_like( '_transient_timeout_' ) . '%', time() ) );
193
194
		// Trigger action
195
		do_action( 'woocommerce_installed' );
196
	}
197
198
	/**
199
	 * Update WC version to current.
200
	 */
201
	private static function update_wc_version() {
202
		delete_option( 'woocommerce_version' );
203
		add_option( 'woocommerce_version', WC()->version );
204
	}
205
206
	/**
207
	 * Push all needed DB updates to the queue for processing.
208
	 */
209
	private static function update() {
210
		$current_db_version = get_option( 'woocommerce_db_version' );
211
		$logger             = new WC_Logger();
212
		$update_queued      = false;
213
214
		foreach ( self::$db_updates as $version => $update_callbacks ) {
215
			if ( version_compare( $current_db_version, $version, '<' ) ) {
216
				foreach ( $update_callbacks as $update_callback ) {
217
					$logger->add( 'wc_db_updates', sprintf( 'Queuing %s - %s', $version, $update_callback ) );
218
					self::$background_updater->push_to_queue( $update_callback );
219
					$update_queued = true;
220
				}
221
			}
222
		}
223
224
		if ( $update_queued ) {
225
			self::$background_updater->save()->dispatch();
226
		}
227
	}
228
229
	/**
230
	 * Update DB version to current.
231
	 * @param string $version
232
	 */
233
	public static function update_db_version( $version = null ) {
234
		delete_option( 'woocommerce_db_version' );
235
		add_option( 'woocommerce_db_version', is_null( $version ) ? WC()->version : $version );
236
	}
237
238
	/**
239
	 * Add more cron schedules.
240
	 * @param  array $schedules
241
	 * @return array
242
	 */
243
	public static function cron_schedules( $schedules ) {
244
		$schedules['monthly'] = array(
245
			'interval' => 2635200,
246
			'display'  => __( 'Monthly', 'woocommerce' )
247
		);
248
		return $schedules;
249
	}
250
251
	/**
252
	 * Create cron jobs (clear them first).
253
	 */
254
	private static function create_cron_jobs() {
255
		wp_clear_scheduled_hook( 'woocommerce_scheduled_sales' );
256
		wp_clear_scheduled_hook( 'woocommerce_cancel_unpaid_orders' );
257
		wp_clear_scheduled_hook( 'woocommerce_cleanup_sessions' );
258
		wp_clear_scheduled_hook( 'woocommerce_geoip_updater' );
259
		wp_clear_scheduled_hook( 'woocommerce_tracker_send_event' );
260
261
		$ve = get_option( 'gmt_offset' ) > 0 ? '+' : '-';
262
263
		wp_schedule_event( strtotime( '00:00 tomorrow ' . $ve . get_option( 'gmt_offset' ) . ' HOURS' ), 'daily', 'woocommerce_scheduled_sales' );
264
265
		$held_duration = get_option( 'woocommerce_hold_stock_minutes', '60' );
266
267
		if ( $held_duration != '' ) {
268
			wp_schedule_single_event( time() + ( absint( $held_duration ) * 60 ), 'woocommerce_cancel_unpaid_orders' );
269
		}
270
271
		wp_schedule_event( time(), 'twicedaily', 'woocommerce_cleanup_sessions' );
272
		wp_schedule_event( strtotime( 'first tuesday of next month' ), 'monthly', 'woocommerce_geoip_updater' );
273
		wp_schedule_event( time(), apply_filters( 'woocommerce_tracker_event_recurrence', 'daily' ), 'woocommerce_tracker_send_event' );
274
	}
275
276
	/**
277
	 * Create pages that the plugin relies on, storing page id's in variables.
278
	 */
279
	public static function create_pages() {
280
		include_once( 'admin/wc-admin-functions.php' );
281
282
		$pages = apply_filters( 'woocommerce_create_pages', array(
283
			'shop' => array(
284
				'name'    => _x( 'shop', 'Page slug', 'woocommerce' ),
285
				'title'   => _x( 'Shop', 'Page title', 'woocommerce' ),
286
				'content' => ''
287
			),
288
			'cart' => array(
289
				'name'    => _x( 'cart', 'Page slug', 'woocommerce' ),
290
				'title'   => _x( 'Cart', 'Page title', 'woocommerce' ),
291
				'content' => '[' . apply_filters( 'woocommerce_cart_shortcode_tag', 'woocommerce_cart' ) . ']'
292
			),
293
			'checkout' => array(
294
				'name'    => _x( 'checkout', 'Page slug', 'woocommerce' ),
295
				'title'   => _x( 'Checkout', 'Page title', 'woocommerce' ),
296
				'content' => '[' . apply_filters( 'woocommerce_checkout_shortcode_tag', 'woocommerce_checkout' ) . ']'
297
			),
298
			'myaccount' => array(
299
				'name'    => _x( 'my-account', 'Page slug', 'woocommerce' ),
300
				'title'   => _x( 'My Account', 'Page title', 'woocommerce' ),
301
				'content' => '[' . apply_filters( 'woocommerce_my_account_shortcode_tag', 'woocommerce_my_account' ) . ']'
302
			)
303
		) );
304
305
		foreach ( $pages as $key => $page ) {
306
			wc_create_page( esc_sql( $page['name'] ), 'woocommerce_' . $key . '_page_id', $page['title'], $page['content'], ! empty( $page['parent'] ) ? wc_get_page_id( $page['parent'] ) : '' );
307
		}
308
309
		delete_transient( 'woocommerce_cache_excluded_uris' );
310
	}
311
312
	/**
313
	 * Default options.
314
	 *
315
	 * Sets up the default options used on the settings page.
316
	 */
317
	private static function create_options() {
318
		// Include settings so that we can run through defaults
319
		include_once( 'admin/class-wc-admin-settings.php' );
320
321
		$settings = WC_Admin_Settings::get_settings_pages();
322
323
		foreach ( $settings as $section ) {
324
			if ( ! method_exists( $section, 'get_settings' ) ) {
325
				continue;
326
			}
327
			$subsections = array_unique( array_merge( array( '' ), array_keys( $section->get_sections() ) ) );
328
329
			foreach ( $subsections as $subsection ) {
330
				foreach ( $section->get_settings( $subsection ) as $value ) {
331
					if ( isset( $value['default'] ) && isset( $value['id'] ) ) {
332
						$autoload = isset( $value['autoload'] ) ? (bool) $value['autoload'] : true;
333
						add_option( $value['id'], $value['default'], '', ( $autoload ? 'yes' : 'no' ) );
334
					}
335
				}
336
			}
337
		}
338
	}
339
340
	/**
341
	 * Add the default terms for WC taxonomies - product types and order statuses. Modify this at your own risk.
342
	 */
343
	private static function create_terms() {
344
		$taxonomies = array(
345
			'product_type' => array(
346
				'simple',
347
				'grouped',
348
				'variable',
349
				'external'
350
			)
351
		);
352
353
		foreach ( $taxonomies as $taxonomy => $terms ) {
354
			foreach ( $terms as $term ) {
355
				if ( ! get_term_by( 'slug', sanitize_title( $term ), $taxonomy ) ) {
356
					wp_insert_term( $term, $taxonomy );
357
				}
358
			}
359
		}
360
	}
361
362
	/**
363
	 * Set up the database tables which the plugin needs to function.
364
	 *
365
	 * Tables:
366
	 *		woocommerce_attribute_taxonomies - Table for storing attribute taxonomies - these are user defined
367
	 *		woocommerce_termmeta - Term meta table - sadly WordPress does not have termmeta so we need our own
368
	 *		woocommerce_downloadable_product_permissions - Table for storing user and guest download permissions.
369
	 *			KEY(order_id, product_id, download_id) used for organizing downloads on the My Account page
370
	 *		woocommerce_order_items - Order line items are stored in a table to make them easily queryable for reports
371
	 *		woocommerce_order_itemmeta - Order line item meta is stored in a table for storing extra data.
372
	 *		woocommerce_tax_rates - Tax Rates are stored inside 2 tables making tax queries simple and efficient.
373
	 *		woocommerce_tax_rate_locations - Each rate can be applied to more than one postcode/city hence the second table.
374
	 */
375
	private static function create_tables() {
376
		global $wpdb;
377
378
		$wpdb->hide_errors();
379
380
		require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
381
382
		/**
383
		 * Before updating with DBDELTA, remove any primary keys which could be
384
		 * modified due to schema updates.
385
		 */
386 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...
387
			if ( ! $wpdb->get_var( "SHOW COLUMNS FROM `{$wpdb->prefix}woocommerce_downloadable_product_permissions` LIKE 'permission_id';" ) ) {
388
				$wpdb->query( "ALTER TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions DROP PRIMARY KEY, ADD `permission_id` bigint(20) NOT NULL PRIMARY KEY AUTO_INCREMENT;" );
389
			}
390
		}
391
392
		dbDelta( self::get_schema() );
393
	}
394
395
	/**
396
	 * Get Table schema.
397
	 * https://github.com/woothemes/woocommerce/wiki/Database-Description/
398
	 * @return string
399
	 */
400
	private static function get_schema() {
401
		global $wpdb;
402
403
		$collate = '';
404
405
		if ( $wpdb->has_cap( 'collation' ) ) {
406
			$collate = $wpdb->get_charset_collate();
407
		}
408
409
		/*
410
		 * Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that.
411
		 * As of WordPress 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which
412
		 * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters.
413
		 *
414
		 * This may cause duplicate index notices in logs due to https://core.trac.wordpress.org/ticket/34870 but dropping
415
		 * indexes first causes too much load on some servers/larger DB.
416
		 */
417
		$max_index_length = 191;
418
419
		$tables = "
420
CREATE TABLE {$wpdb->prefix}woocommerce_sessions (
421
  session_id bigint(20) NOT NULL AUTO_INCREMENT,
422
  session_key char(32) NOT NULL,
423
  session_value longtext NOT NULL,
424
  session_expiry bigint(20) NOT NULL,
425
  UNIQUE KEY session_id (session_id),
426
  PRIMARY KEY  (session_key)
427
) $collate;
428
CREATE TABLE {$wpdb->prefix}woocommerce_api_keys (
429
  key_id bigint(20) NOT NULL auto_increment,
430
  user_id bigint(20) NOT NULL,
431
  description longtext NULL,
432
  permissions varchar(10) NOT NULL,
433
  consumer_key char(64) NOT NULL,
434
  consumer_secret char(43) NOT NULL,
435
  nonces longtext NULL,
436
  truncated_key char(7) NOT NULL,
437
  last_access datetime NULL default null,
438
  PRIMARY KEY  (key_id),
439
  KEY consumer_key (consumer_key),
440
  KEY consumer_secret (consumer_secret)
441
) $collate;
442
CREATE TABLE {$wpdb->prefix}woocommerce_attribute_taxonomies (
443
  attribute_id bigint(20) NOT NULL auto_increment,
444
  attribute_name varchar(200) NOT NULL,
445
  attribute_label longtext NULL,
446
  attribute_type varchar(200) NOT NULL,
447
  attribute_orderby varchar(200) NOT NULL,
448
  attribute_public int(1) NOT NULL DEFAULT 1,
449
  PRIMARY KEY  (attribute_id),
450
  KEY attribute_name (attribute_name($max_index_length))
451
) $collate;
452
CREATE TABLE {$wpdb->prefix}woocommerce_downloadable_product_permissions (
453
  permission_id bigint(20) NOT NULL auto_increment,
454
  download_id varchar(32) NOT NULL,
455
  product_id bigint(20) NOT NULL,
456
  order_id bigint(20) NOT NULL DEFAULT 0,
457
  order_key varchar(200) NOT NULL,
458
  user_email varchar(200) NOT NULL,
459
  user_id bigint(20) NULL,
460
  downloads_remaining varchar(9) NULL,
461
  access_granted datetime NOT NULL default '0000-00-00 00:00:00',
462
  access_expires datetime NULL default null,
463
  download_count bigint(20) NOT NULL DEFAULT 0,
464
  PRIMARY KEY  (permission_id),
465
  KEY download_order_key_product (product_id,order_id,order_key($max_index_length),download_id),
466
  KEY download_order_product (download_id,order_id,product_id)
467
) $collate;
468
CREATE TABLE {$wpdb->prefix}woocommerce_order_items (
469
  order_item_id bigint(20) NOT NULL auto_increment,
470
  order_item_name longtext NOT NULL,
471
  order_item_type varchar(200) NOT NULL DEFAULT '',
472
  order_id bigint(20) NOT NULL,
473
  PRIMARY KEY  (order_item_id),
474
  KEY order_id (order_id)
475
) $collate;
476
CREATE TABLE {$wpdb->prefix}woocommerce_order_itemmeta (
477
  meta_id bigint(20) NOT NULL auto_increment,
478
  order_item_id bigint(20) NOT NULL,
479
  meta_key varchar(255) default NULL,
480
  meta_value longtext NULL,
481
  PRIMARY KEY  (meta_id),
482
  KEY order_item_id (order_item_id),
483
  KEY meta_key (meta_key($max_index_length))
484
) $collate;
485
CREATE TABLE {$wpdb->prefix}woocommerce_tax_rates (
486
  tax_rate_id bigint(20) NOT NULL auto_increment,
487
  tax_rate_country varchar(200) NOT NULL DEFAULT '',
488
  tax_rate_state varchar(200) NOT NULL DEFAULT '',
489
  tax_rate varchar(200) NOT NULL DEFAULT '',
490
  tax_rate_name varchar(200) NOT NULL DEFAULT '',
491
  tax_rate_priority bigint(20) NOT NULL,
492
  tax_rate_compound int(1) NOT NULL DEFAULT 0,
493
  tax_rate_shipping int(1) NOT NULL DEFAULT 1,
494
  tax_rate_order bigint(20) NOT NULL,
495
  tax_rate_class varchar(200) NOT NULL DEFAULT '',
496
  PRIMARY KEY  (tax_rate_id),
497
  KEY tax_rate_country (tax_rate_country($max_index_length)),
498
  KEY tax_rate_state (tax_rate_state($max_index_length)),
499
  KEY tax_rate_class (tax_rate_class($max_index_length)),
500
  KEY tax_rate_priority (tax_rate_priority)
501
) $collate;
502
CREATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations (
503
  location_id bigint(20) NOT NULL auto_increment,
504
  location_code varchar(255) NOT NULL,
505
  tax_rate_id bigint(20) NOT NULL,
506
  location_type varchar(40) NOT NULL,
507
  PRIMARY KEY  (location_id),
508
  KEY tax_rate_id (tax_rate_id),
509
  KEY location_type (location_type),
510
  KEY location_type_code (location_type(40),location_code(90))
511
) $collate;
512
CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zones (
513
  zone_id bigint(20) NOT NULL auto_increment,
514
  zone_name varchar(255) NOT NULL,
515
  zone_order bigint(20) NOT NULL,
516
  PRIMARY KEY  (zone_id)
517
) $collate;
518
CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_locations (
519
  location_id bigint(20) NOT NULL auto_increment,
520
  zone_id bigint(20) NOT NULL,
521
  location_code varchar(255) NOT NULL,
522
  location_type varchar(40) NOT NULL,
523
  PRIMARY KEY  (location_id),
524
  KEY location_id (location_id),
525
  KEY location_type (location_type),
526
  KEY location_type_code (location_type(40),location_code(90))
527
) $collate;
528
CREATE TABLE {$wpdb->prefix}woocommerce_shipping_zone_methods (
529
  zone_id bigint(20) NOT NULL,
530
  instance_id bigint(20) NOT NULL auto_increment,
531
  method_id varchar(255) NOT NULL,
532
  method_order bigint(20) NOT NULL,
533
  is_enabled tinyint(1) NOT NULL DEFAULT '1',
534
  PRIMARY KEY  (instance_id)
535
) $collate;
536
CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokens (
537
  token_id bigint(20) NOT NULL auto_increment,
538
  gateway_id varchar(255) NOT NULL,
539
  token text NOT NULL,
540
  user_id bigint(20) NOT NULL DEFAULT '0',
541
  type varchar(255) NOT NULL,
542
  is_default tinyint(1) NOT NULL DEFAULT '0',
543
  PRIMARY KEY  (token_id),
544
  KEY user_id (user_id)
545
) $collate;
546
CREATE TABLE {$wpdb->prefix}woocommerce_payment_tokenmeta (
547
  meta_id bigint(20) NOT NULL auto_increment,
548
  payment_token_id bigint(20) NOT NULL,
549
  meta_key varchar(255) NULL,
550
  meta_value longtext NULL,
551
  PRIMARY KEY  (meta_id),
552
  KEY payment_token_id (payment_token_id),
553
  KEY meta_key (meta_key)
554
) $collate;
555
		";
556
557
		// Term meta is only needed for old installs.
558
		if ( ! function_exists( 'get_term_meta' ) ) {
559
			$tables .= "
560
CREATE TABLE {$wpdb->prefix}woocommerce_termmeta (
561
  meta_id bigint(20) NOT NULL auto_increment,
562
  woocommerce_term_id bigint(20) NOT NULL,
563
  meta_key varchar(255) default NULL,
564
  meta_value longtext NULL,
565
  PRIMARY KEY  (meta_id),
566
  KEY woocommerce_term_id (woocommerce_term_id),
567
  KEY meta_key (meta_key($max_index_length))
568
) $collate;
569
			";
570
		}
571
572
		return $tables;
573
	}
574
575
	/**
576
	 * Create roles and capabilities.
577
	 */
578
	public static function create_roles() {
579
		global $wp_roles;
580
581
		if ( ! class_exists( 'WP_Roles' ) ) {
582
			return;
583
		}
584
585
		if ( ! isset( $wp_roles ) ) {
586
			$wp_roles = new WP_Roles();
587
		}
588
589
		// Customer role
590
		add_role( 'customer', __( 'Customer', 'woocommerce' ), array(
591
			'read' 					=> true
592
		) );
593
594
		// Shop manager role
595
		add_role( 'shop_manager', __( 'Shop Manager', 'woocommerce' ), array(
596
			'level_9'                => true,
597
			'level_8'                => true,
598
			'level_7'                => true,
599
			'level_6'                => true,
600
			'level_5'                => true,
601
			'level_4'                => true,
602
			'level_3'                => true,
603
			'level_2'                => true,
604
			'level_1'                => true,
605
			'level_0'                => true,
606
			'read'                   => true,
607
			'read_private_pages'     => true,
608
			'read_private_posts'     => true,
609
			'edit_users'             => true,
610
			'edit_posts'             => true,
611
			'edit_pages'             => true,
612
			'edit_published_posts'   => true,
613
			'edit_published_pages'   => true,
614
			'edit_private_pages'     => true,
615
			'edit_private_posts'     => true,
616
			'edit_others_posts'      => true,
617
			'edit_others_pages'      => true,
618
			'publish_posts'          => true,
619
			'publish_pages'          => true,
620
			'delete_posts'           => true,
621
			'delete_pages'           => true,
622
			'delete_private_pages'   => true,
623
			'delete_private_posts'   => true,
624
			'delete_published_pages' => true,
625
			'delete_published_posts' => true,
626
			'delete_others_posts'    => true,
627
			'delete_others_pages'    => true,
628
			'manage_categories'      => true,
629
			'manage_links'           => true,
630
			'moderate_comments'      => true,
631
			'unfiltered_html'        => true,
632
			'upload_files'           => true,
633
			'export'                 => true,
634
			'import'                 => true,
635
			'list_users'             => true
636
		) );
637
638
		$capabilities = self::get_core_capabilities();
639
640
		foreach ( $capabilities as $cap_group ) {
641
			foreach ( $cap_group as $cap ) {
642
				$wp_roles->add_cap( 'shop_manager', $cap );
643
				$wp_roles->add_cap( 'administrator', $cap );
644
			}
645
		}
646
	}
647
648
	/**
649
	 * Get capabilities for WooCommerce - these are assigned to admin/shop manager during installation or reset.
650
	 *
651
	 * @return array
652
	 */
653
	 private static function get_core_capabilities() {
654
		$capabilities = array();
655
656
		$capabilities['core'] = array(
657
			'manage_woocommerce',
658
			'view_woocommerce_reports'
659
		);
660
661
		$capability_types = array( 'product', 'shop_order', 'shop_coupon', 'shop_webhook' );
662
663
		foreach ( $capability_types as $capability_type ) {
664
665
			$capabilities[ $capability_type ] = array(
666
				// Post type
667
				"edit_{$capability_type}",
668
				"read_{$capability_type}",
669
				"delete_{$capability_type}",
670
				"edit_{$capability_type}s",
671
				"edit_others_{$capability_type}s",
672
				"publish_{$capability_type}s",
673
				"read_private_{$capability_type}s",
674
				"delete_{$capability_type}s",
675
				"delete_private_{$capability_type}s",
676
				"delete_published_{$capability_type}s",
677
				"delete_others_{$capability_type}s",
678
				"edit_private_{$capability_type}s",
679
				"edit_published_{$capability_type}s",
680
681
				// Terms
682
				"manage_{$capability_type}_terms",
683
				"edit_{$capability_type}_terms",
684
				"delete_{$capability_type}_terms",
685
				"assign_{$capability_type}_terms"
686
			);
687
		}
688
689
		return $capabilities;
690
	}
691
692
	/**
693
	 * woocommerce_remove_roles function.
694
	 */
695
	public static function remove_roles() {
696
		global $wp_roles;
697
698
		if ( ! class_exists( 'WP_Roles' ) ) {
699
			return;
700
		}
701
702
		if ( ! isset( $wp_roles ) ) {
703
			$wp_roles = new WP_Roles();
704
		}
705
706
		$capabilities = self::get_core_capabilities();
707
708
		foreach ( $capabilities as $cap_group ) {
709
			foreach ( $cap_group as $cap ) {
710
				$wp_roles->remove_cap( 'shop_manager', $cap );
711
				$wp_roles->remove_cap( 'administrator', $cap );
712
			}
713
		}
714
715
		remove_role( 'customer' );
716
		remove_role( 'shop_manager' );
717
	}
718
719
	/**
720
	 * Create files/directories.
721
	 */
722
	private static function create_files() {
723
		// Install files and folders for uploading files and prevent hotlinking
724
		$upload_dir      = wp_upload_dir();
725
		$download_method = get_option( 'woocommerce_file_download_method', 'force' );
726
727
		$files = array(
728
			array(
729
				'base' 		=> $upload_dir['basedir'] . '/woocommerce_uploads',
730
				'file' 		=> 'index.html',
731
				'content' 	=> ''
732
			),
733
			array(
734
				'base' 		=> WC_LOG_DIR,
735
				'file' 		=> '.htaccess',
736
				'content' 	=> 'deny from all'
737
			),
738
			array(
739
				'base' 		=> WC_LOG_DIR,
740
				'file' 		=> 'index.html',
741
				'content' 	=> ''
742
			)
743
		);
744
745
		if ( 'redirect' !== $download_method ) {
746
			$files[] = array(
747
				'base' 		=> $upload_dir['basedir'] . '/woocommerce_uploads',
748
				'file' 		=> '.htaccess',
749
				'content' 	=> 'deny from all'
750
			);
751
		}
752
753
		foreach ( $files as $file ) {
754
			if ( wp_mkdir_p( $file['base'] ) && ! file_exists( trailingslashit( $file['base'] ) . $file['file'] ) ) {
755
				if ( $file_handle = @fopen( trailingslashit( $file['base'] ) . $file['file'], 'w' ) ) {
756
					fwrite( $file_handle, $file['content'] );
757
					fclose( $file_handle );
758
				}
759
			}
760
		}
761
	}
762
763
	/**
764
	 * Show plugin changes. Code adapted from W3 Total Cache.
765
	 */
766
	public static function in_plugin_update_message( $args ) {
767
		$transient_name = 'wc_upgrade_notice_' . $args['Version'];
768
769
		if ( false === ( $upgrade_notice = get_transient( $transient_name ) ) ) {
770
			$response = wp_safe_remote_get( 'https://plugins.svn.wordpress.org/woocommerce/trunk/readme.txt' );
771
772
			if ( ! is_wp_error( $response ) && ! empty( $response['body'] ) ) {
773
				$upgrade_notice = self::parse_update_notice( $response['body'], $args['new_version'] );
774
				set_transient( $transient_name, $upgrade_notice, DAY_IN_SECONDS );
775
			}
776
		}
777
778
		echo wp_kses_post( $upgrade_notice );
779
	}
780
781
	/**
782
	 * Parse update notice from readme file.
783
	 *
784
	 * @param  string $content
785
	 * @param  string $new_version
786
	 * @return string
787
	 */
788
	private static function parse_update_notice( $content, $new_version ) {
789
		// Output Upgrade Notice.
790
		$matches        = null;
791
		$regexp         = '~==\s*Upgrade Notice\s*==\s*=\s*(.*)\s*=(.*)(=\s*' . preg_quote( WC_VERSION ) . '\s*=|$)~Uis';
792
		$upgrade_notice = '';
793
794
		if ( preg_match( $regexp, $content, $matches ) ) {
795
			$version = trim( $matches[1] );
796
			$notices = (array) preg_split('~[\r\n]+~', trim( $matches[2] ) );
797
798
			// Check the latest stable version and ignore trunk.
799
			if ( $version === $new_version && version_compare( WC_VERSION, $version, '<' ) ) {
800
801
				$upgrade_notice .= '<div class="wc_plugin_upgrade_notice">';
802
803
				foreach ( $notices as $index => $line ) {
804
					$upgrade_notice .= wp_kses_post( preg_replace( '~\[([^\]]*)\]\(([^\)]*)\)~', '<a href="${2}">${1}</a>', $line ) );
805
				}
806
807
				$upgrade_notice .= '</div> ';
808
			}
809
		}
810
811
		return wp_kses_post( $upgrade_notice );
812
	}
813
814
	/**
815
	 * Show action links on the plugin screen.
816
	 *
817
	 * @param	mixed $links Plugin Action links
818
	 * @return	array
819
	 */
820
	public static function plugin_action_links( $links ) {
821
		$action_links = array(
822
			'settings' => '<a href="' . admin_url( 'admin.php?page=wc-settings' ) . '" title="' . esc_attr( __( 'View WooCommerce Settings', 'woocommerce' ) ) . '">' . __( 'Settings', 'woocommerce' ) . '</a>',
823
		);
824
825
		return array_merge( $action_links, $links );
826
	}
827
828
	/**
829
	 * Show row meta on the plugin screen.
830
	 *
831
	 * @param	mixed $links Plugin Row Meta
832
	 * @param	mixed $file  Plugin Base file
833
	 * @return	array
834
	 */
835
	public static function plugin_row_meta( $links, $file ) {
836
		if ( $file == WC_PLUGIN_BASENAME ) {
837
			$row_meta = array(
838
				'docs'    => '<a href="' . esc_url( apply_filters( 'woocommerce_docs_url', 'https://docs.woothemes.com/documentation/plugins/woocommerce/' ) ) . '" title="' . esc_attr( __( 'View WooCommerce Documentation', 'woocommerce' ) ) . '">' . __( 'Docs', 'woocommerce' ) . '</a>',
839
				'apidocs' => '<a href="' . esc_url( apply_filters( 'woocommerce_apidocs_url', 'https://docs.woothemes.com/wc-apidocs/' ) ) . '" title="' . esc_attr( __( 'View WooCommerce API Docs', 'woocommerce' ) ) . '">' . __( 'API Docs', 'woocommerce' ) . '</a>',
840
				'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>',
841
			);
842
843
			return array_merge( $links, $row_meta );
844
		}
845
846
		return (array) $links;
847
	}
848
849
	/**
850
	 * Uninstall tables when MU blog is deleted.
851
	 * @param  array $tables
852
	 * @return string[]
853
	 */
854
	public static function wpmu_drop_tables( $tables ) {
855
		global $wpdb;
856
857
		$tables[] = $wpdb->prefix . 'woocommerce_sessions';
858
		$tables[] = $wpdb->prefix . 'woocommerce_api_keys';
859
		$tables[] = $wpdb->prefix . 'woocommerce_attribute_taxonomies';
860
		$tables[] = $wpdb->prefix . 'woocommerce_downloadable_product_permissions';
861
		$tables[] = $wpdb->prefix . 'woocommerce_termmeta';
862
		$tables[] = $wpdb->prefix . 'woocommerce_tax_rates';
863
		$tables[] = $wpdb->prefix . 'woocommerce_tax_rate_locations';
864
		$tables[] = $wpdb->prefix . 'woocommerce_order_items';
865
		$tables[] = $wpdb->prefix . 'woocommerce_order_itemmeta';
866
867
		return $tables;
868
	}
869
870
	/**
871
	 * Get slug from path
872
	 * @param  string $key
873
	 * @return string
874
	 */
875
	private static function format_plugin_slug( $key ) {
876
		$slug = explode( '/', $key );
877
		$slug = explode( '.', end( $slug ) );
878
		return $slug[0];
879
	}
880
881
	/**
882
	 * Install a plugin from .org in the background via a cron job (used by
883
	 * installer - opt in).
884
	 * @param string $plugin_to_install_id
885
	 * @param array $plugin_to_install
886
	 * @since 2.6.0
887
	 */
888
	public static function background_installer( $plugin_to_install_id, $plugin_to_install ) {
889
		if ( ! empty( $plugin_to_install['repo-slug'] ) ) {
890
			require_once( ABSPATH . 'wp-admin/includes/file.php' );
891
			require_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
892
			require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
893
			require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
894
895
			WP_Filesystem();
896
897
			$skin              = new Automatic_Upgrader_Skin;
898
			$upgrader          = new WP_Upgrader( $skin );
899
			$installed_plugins = array_map( array( __CLASS__, 'format_plugin_slug' ), array_keys( get_plugins() ) );
900
			$plugin_slug       = $plugin_to_install['repo-slug'];
901
			$plugin            = $plugin_slug . '/' . $plugin_slug . '.php';
902
			$installed         = false;
903
			$activate          = false;
904
905
			// See if the plugin is installed already
906
			if ( in_array( $plugin_to_install['repo-slug'], $installed_plugins ) ) {
907
				$installed = true;
908
				$activate  = ! is_plugin_active( $plugin );
909
			}
910
911
			// Install this thing!
912
			if ( ! $installed ) {
913
				// Suppress feedback
914
				ob_start();
915
916
				try {
917
					$plugin_information = plugins_api( 'plugin_information', array(
918
						'slug'   => $plugin_to_install['repo-slug'],
919
						'fields' => array(
920
							'short_description' => false,
921
							'sections'          => false,
922
							'requires'          => false,
923
							'rating'            => false,
924
							'ratings'           => false,
925
							'downloaded'        => false,
926
							'last_updated'      => false,
927
							'added'             => false,
928
							'tags'              => false,
929
							'homepage'          => false,
930
							'donate_link'       => false,
931
							'author_profile'    => false,
932
							'author'            => false,
933
						),
934
					) );
935
936
					if ( is_wp_error( $plugin_information ) ) {
937
						throw new Exception( $plugin_information->get_error_message() );
938
					}
939
940
					$package  = $plugin_information->download_link;
941
					$download = $upgrader->download_package( $package );
942
943
					if ( is_wp_error( $download ) ) {
944
						throw new Exception( $download->get_error_message() );
945
					}
946
947
					$working_dir = $upgrader->unpack_package( $download, true );
948
949
					if ( is_wp_error( $working_dir ) ) {
950
						throw new Exception( $working_dir->get_error_message() );
951
					}
952
953
					$result = $upgrader->install_package( array(
954
						'source'                      => $working_dir,
955
						'destination'                 => WP_PLUGIN_DIR,
956
						'clear_destination'           => false,
957
						'abort_if_destination_exists' => false,
958
						'clear_working'               => true,
959
						'hook_extra'                  => array(
960
							'type'   => 'plugin',
961
							'action' => 'install',
962
						),
963
					) );
964
965
					if ( is_wp_error( $result ) ) {
966
						throw new Exception( $result->get_error_message() );
967
					}
968
969
					$activate = true;
970
971
				} catch ( Exception $e ) {
972
					WC_Admin_Notices::add_custom_notice(
973
						$plugin_to_install_id . '_install_error',
974
						sprintf(
975
							__( '%1$s could not be installed (%2$s). %3$sPlease install it manually by clicking here.%4$s', 'woocommerce' ),
976
							$plugin_to_install['name'],
977
							$e->getMessage(),
978
							'<a href="' . esc_url( admin_url( 'index.php?wc-install-plugin-redirect=' . $plugin_to_install['repo-slug'] ) ) . '">',
979
							'</a>'
980
						)
981
					);
982
				}
983
984
				// Discard feedback
985
				ob_end_clean();
986
			}
987
988
			wp_clean_plugins_cache();
989
990
			// Activate this thing
991
			if ( $activate ) {
992
				try {
993
					$result = activate_plugin( $plugin );
994
995
					if ( is_wp_error( $result ) ) {
996
						throw new Exception( $result->get_error_message() );
997
					}
998
999
				} catch ( Exception $e ) {
1000
					WC_Admin_Notices::add_custom_notice(
1001
						$plugin_to_install_id . '_install_error',
1002
						sprintf(
1003
							__( '%1$s was installed but could not be activated. %2$sPlease activate it manually by clicking here.%3$s', 'woocommerce' ),
1004
							$plugin_to_install['name'],
1005
							'<a href="' . admin_url( 'plugins.php' ) . '">',
1006
							'</a>'
1007
						)
1008
					);
1009
				}
1010
			}
1011
		}
1012
	}
1013
}
1014
1015
WC_Install::init();
1016