This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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
|
|||
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 |
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.