Completed
Pull Request — master (#10632)
by Mike
12:22
created

WC_Install::format_plugin_slug()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 18 and the first side effect is on line 12.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 that need to be run */
21
	private static $db_updates = array(
22
		'2.0.0' => 'updates/woocommerce-update-2.0.php',
23
		'2.0.9' => 'updates/woocommerce-update-2.0.9.php',
24
		'2.1.0' => 'updates/woocommerce-update-2.1.php',
25
		'2.2.0' => 'updates/woocommerce-update-2.2.php',
26
		'2.3.0' => 'updates/woocommerce-update-2.3.php',
27
		'2.4.0' => 'updates/woocommerce-update-2.4.php',
28
		'2.4.1' => 'updates/woocommerce-update-2.4.1.php',
29
		'2.5.0' => 'updates/woocommerce-update-2.5.php',
30
		'2.6.0' => 'updates/woocommerce-update-2.6.php'
31
	);
32
33
	/**
34
	 * Hook in tabs.
35
	 */
36
	public static function init() {
37
		add_action( 'init', array( __CLASS__, 'check_version' ), 5 );
38
		add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
39
		add_action( 'in_plugin_update_message-woocommerce/woocommerce.php', array( __CLASS__, 'in_plugin_update_message' ) );
40
		add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
41
		add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
42
		add_filter( 'wpmu_drop_tables', array( __CLASS__, 'wpmu_drop_tables' ) );
43
		add_filter( 'cron_schedules', array( __CLASS__, 'cron_schedules' ) );
44
		add_action( 'woocommerce_plugin_background_installer', array( __CLASS__, 'background_installer' ), 10, 2 );
45
	}
46
47
	/**
48
	 * Check WooCommerce version and run the updater is required.
49
	 *
50
	 * This check is done on all requests and runs if he versions do not match.
51
	 */
52
	public static function check_version() {
53
		if ( ! defined( 'IFRAME_REQUEST' ) && get_option( 'woocommerce_version' ) !== WC()->version ) {
54
			self::install();
55
			do_action( 'woocommerce_updated' );
56
		}
57
	}
58
59
	/**
60
	 * Install actions when a update button is clicked within the admin area.
61
	 *
62
	 * This function is hooked into admin_init to affect admin only.
63
	 */
64
	public static function install_actions() {
65
		if ( ! empty( $_GET['do_update_woocommerce'] ) ) {
66
			self::update();
67
			WC_Admin_Notices::remove_notice( 'update' );
68
			add_action( 'admin_notices', array( __CLASS__, 'updated_notice' ) );
69
		}
70
	}
71
72
	/**
73
	 * Show notice stating update was successful.
74
	 */
75
	public static function updated_notice() {
76
		?>
77
		<div id="message" class="updated woocommerce-message wc-connect">
78
			<p><?php _e( 'WooCommerce data update complete. Thank you for updating to the latest version!', 'woocommerce' ); ?></p>
79
		</div>
80
		<?php
81
	}
82
83
	/**
84
	 * Install WC.
85
	 */
86
	public static function install() {
87
		global $wpdb;
88
89
		if ( ! defined( 'WC_INSTALLING' ) ) {
90
			define( 'WC_INSTALLING', true );
91
		}
92
93
		// Ensure needed classes are loaded
94
		include_once( 'admin/class-wc-admin-notices.php' );
95
96
		self::create_options();
97
		self::create_tables();
98
		self::create_roles();
99
100
		// Register post types
101
		WC_Post_types::register_post_types();
102
		WC_Post_types::register_taxonomies();
103
104
		// Also register endpoints - this needs to be done prior to rewrite rule flush
105
		WC()->query->init_query_vars();
106
		WC()->query->add_endpoints();
107
		WC_API::add_endpoint();
108
		WC_Auth::add_endpoint();
109
110
		self::create_terms();
111
		self::create_cron_jobs();
112
		self::create_files();
113
114
		// Queue upgrades/setup wizard
115
		$current_wc_version    = get_option( 'woocommerce_version', null );
116
		$current_db_version    = get_option( 'woocommerce_db_version', null );
117
		$major_wc_version      = substr( WC()->version, 0, strrpos( WC()->version, '.' ) );
0 ignored issues
show
Unused Code introduced by
$major_wc_version is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

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