Completed
Push — master ( 5f46f6...9c1aa8 )
by Mike
09:11 queued 21s
created

WC_Install::update_db_version()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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