PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / migration.php

migration.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/migration.php

439 lines 17.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * DB Migration
4 *
5 * @package StoreEngine
6 */
7
8 namespace StoreEngine;
9
10 use StoreEngine;
11 use StoreEngine\Admin\Settings\Base;
12 use StoreEngine\Database\CreateEmailLog;
13 use StoreEngine\Database\CreateShippingZoneLocations;
14 use StoreEngine\Utils\Fs;
15 use StoreEngine\Utils\Helper;
16 use StoreEngine\Admin\Settings;
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 class Migration {
23
24 public static function init() {
25 $self = new self();
26 add_action( 'init', [ $self, 'run_migration' ] );
27 }
28
29 public function run_migration() {
30 // @TODO refactor this with:
31 // - Update the original schema and remove the alter queries as db-delta can
32 // generate alter query when needed.
33 // - Progressive data migration with background task queue.
34 // - Run migration on update from installer (not on every init).
35
36 $this->update_admin_role();
37
38 $storeengine_version = get_option( 'storeengine_version' );
39
40 if ( ! get_option( 'storeengine_db_version' ) ) {
41 Database::create_initial_custom_table();
42 Settings::save_settings();
43 update_option( 'storeengine_version', STOREENGINE_VERSION );
44
45 return;
46 }
47
48 // Force update existence templates with new shortcodes.
49 $this->migrate_1_beta_5_1( $storeengine_version );
50 $this->migrate_1_beta_6( $storeengine_version );
51 $this->migrate_stable_1( $storeengine_version );
52 $this->migrate_1_0_2( $storeengine_version );
53 $this->migrate_1_2_1( $storeengine_version );
54 $this->migrate_1_3_0( $storeengine_version );
55 $this->migrate_1_3_3( $storeengine_version );
56 $this->migrate_1_5_0( $storeengine_version );
57 $this->migrate_1_5_2( $storeengine_version );
58 $this->migrate_1_5_5( $storeengine_version );
59 $this->migrate_1_8_0( $storeengine_version );
60 $this->migrate_1_10_0( $storeengine_version );
61
62 // Flag-gated, so it must stay above the save_settings() call below.
63 $this->migrate_sticky_add_to_cart_key();
64 $this->migrate_coupon_end_timestamp();
65
66 if ( STOREENGINE_VERSION !== $storeengine_version ) {
67 Settings::save_settings();
68 update_option( 'storeengine_version', STOREENGINE_VERSION );
69 }
70 }
71
72 public function update_admin_role() {
73 // Current user have administrator role and not have storeengine_shop_manager role then assign storeengine_shop_manager role
74 if ( ! is_user_logged_in() || ! get_current_user_id() ) {
75 return;
76 }
77
78 $user = wp_get_current_user();
79
80 if ( in_array( 'administrator', $user->roles, true ) && ! in_array( 'storeengine_shop_manager', $user->roles, true ) ) {
81 $user->add_role( 'storeengine_shop_manager' );
82 }
83 }
84
85 public function migrate_1_beta_5_1( $storeengine_version ) {
86 if ( version_compare( $storeengine_version, '1.0.0-beta-5.1', '<' ) ) {
87 StoreEngine::init()->load_cart();
88 Helper::create_initial_pages();
89
90 $pages = [ 'cart_page', 'checkout_page', 'thankyou_page', 'dashboard_page' ];
91 foreach ( $pages as $page ) {
92 $page_id = Helper::get_settings( $page );
93 if ( $page_id ) {
94 $page_content_path = STOREENGINE_TEMPLATE_PATH . 'page-content/' . str_replace( '_page', '', $page ) . '.php';
95 if ( file_exists( $page_content_path ) ) {
96 wp_update_post( [
97 'ID' => $page_id,
98 'post_content' => Fs::render_template( $page_content_path ),
99 ] );
100 }
101 }
102 }
103 }
104 }
105
106 // phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.SlowDBQuery.slow_db_query_meta_value, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key
107
108 public function migrate_1_beta_6( $storeengine_version ) {
109 if ( version_compare( $storeengine_version, '1.0.0-beta-6', '<' ) ) {
110 global $wpdb;
111 $method_table = $wpdb->prefix . 'storeengine_shipping_zone_methods';
112 $zone_table = $wpdb->prefix . 'storeengine_shipping_zones';
113
114 $wpdb->query( "ALTER TABLE $method_table DROP COLUMN `type`, DROP COLUMN `cost`, DROP COLUMN `tax`" );
115 $wpdb->query( "ALTER TABLE $method_table
116 ADD COLUMN `method_id` varchar(200) NOT NULL AFTER `zone_id`,
117 ADD COLUMN `settings` longtext NULL AFTER `description`,
118 ADD COLUMN `method_order` bigint(20) UNSIGNED NOT NULL AFTER `method_id`" );
119 $wpdb->query( "ALTER TABLE $zone_table DROP COLUMN `region`" );
120 $wpdb->query( "ALTER TABLE $zone_table ADD COLUMN `zone_order` BIGINT(20) UNSIGNED NOT NULL AFTER `zone_name`" );
121
122 if ( ! function_exists( 'dbDelta' ) ) {
123 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
124 }
125
126 CreateShippingZoneLocations::up( $wpdb->prefix, $wpdb->get_charset_collate() );
127
128 $page = 'thankyou_page';
129 $page_id = Helper::get_settings( $page );
130
131 if ( $page_id ) {
132 $page_content_path = STOREENGINE_TEMPLATE_PATH . 'page-content/' . str_replace( '_page', '', $page ) . '.php';
133 if ( file_exists( $page_content_path ) ) {
134 wp_update_post( [
135 'ID' => $page_id,
136 'post_content' => Fs::render_template( $page_content_path ),
137 ] );
138 }
139 }
140 }
141 }
142
143 public function migrate_stable_1( $storeengine_version ) {
144 if ( version_compare( $storeengine_version, '1.0.0', '<' ) ) {
145 $settings = get_option( STOREENGINE_SETTINGS_NAME );
146 $settings = json_decode( $settings, true );
147
148 if ( 'leftWithSpace' === $settings['store_currency_position'] ) {
149 $settings['store_currency_position'] = 'left_space';
150 }
151 if ( 'rightWithSpace' === $settings['store_currency_position'] ) {
152 $settings['store_currency_position'] = 'right_space';
153 }
154
155 if ( isset( $settings['store_zip'] ) ) {
156 unset( $settings['store_zip'] );
157 }
158
159 update_option( STOREENGINE_SETTINGS_NAME, wp_json_encode( $settings ) );
160 }
161 }
162
163 public function migrate_1_0_2( $storeengine_version ) {
164 if ( version_compare( $storeengine_version, '1.0.2', '<' ) ) {
165 global $wpdb;
166 $users_metadata = $wpdb->get_results( "SELECT * FROM $wpdb->usermeta WHERE meta_key = '_storeengine_purchased_membership_ids'" );
167
168 foreach ( $users_metadata as $user_meta ) {
169 $membership_ids = maybe_unserialize( $user_meta->meta_value );
170 if ( is_array( $membership_ids ) ) {
171 continue;
172 }
173 $membership_ids = maybe_unserialize( $membership_ids );
174 update_user_meta( $user_meta->user_id, '_storeengine_purchased_membership_ids', $membership_ids );
175 }
176
177 if ( ! Helper::get_settings( 'product_archive_filters' ) ) {
178 Settings::save_settings();
179 Base::save_settings( [
180 'product_archive_filters' => [
181 [
182 'slug' => 'search',
183 'status' => true,
184 'order' => 0,
185 ],
186 [
187 'slug' => 'category',
188 'status' => true,
189 'order' => 1,
190 ],
191 [
192 'slug' => 'tags',
193 'status' => true,
194 'order' => 2,
195 ],
196 ],
197 ] );
198 }
199 }
200 }
201
202 public function migrate_1_2_1( $storeengine_version ) {
203 if ( version_compare( $storeengine_version, '1.2.1', '<' ) ) {
204 Settings::save_settings();
205 Base::save_settings( [
206 'product_archive_filters' => [
207 'search' => [
208 'status' => true,
209 'order' => 0,
210 ],
211 'category' => [
212 'status' => true,
213 'order' => 1,
214 ],
215 'tags' => [
216 'status' => true,
217 'order' => 2,
218 ],
219 ],
220 'auth_redirect_type' => 'storeengine',
221 'auth_redirect_url' => '',
222 ] );
223 }
224 }
225
226 public function migrate_1_3_0( $storeengine_version ) {
227 if ( version_compare( $storeengine_version, '1.3.0', '<' ) ) {
228 global $wpdb;
229
230 // Update taxonomy terms for new attribute taxonomy prefix.
231 $wpdb->query( "UPDATE $wpdb->term_taxonomy SET `taxonomy` = REGEXP_REPLACE(`taxonomy`, '^storeengine_pa_', 'se_pa_') WHERE taxonomy LIKE 'storeengine_pa_%'" );
232
233 // Update attribute order data with new attribute taxonomy prefix.
234 $results = $wpdb->get_results( "SELECT meta_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '_storeengine_product_attributes_order' AND meta_value LIKE '%storeengine_pa_%'" );
235
236 foreach ( $results as $meta ) {
237 $meta_value = maybe_unserialize( $meta->meta_value );
238 if ( ! is_array( $meta_value ) ) {
239 $meta_value = json_decode( $meta->meta_value, true );
240 }
241
242 foreach ( $meta_value as $k => $v ) {
243 /** @noinspection RegExpRedundantEscape */
244 $meta_value[ $k ] = Helper::get_attribute_taxonomy_name( preg_replace( '/^storeengine_pa\_/', '', $v ) );
245 }
246
247 $wpdb->update(
248 $wpdb->postmeta,
249 [ 'meta_value' => maybe_serialize( $meta_value ) ],
250 [ 'meta_id' => $meta->meta_id ]
251 );
252 }
253 }
254 }
255
256 public function migrate_1_3_3( $storeengine_version ) {
257 if ( version_compare( $storeengine_version, '1.3.3', '<' ) ) {
258 global $wpdb;
259 wp_cache_flush();
260
261 // Missing migration for PR#803
262 $orders_meta = "{$wpdb->prefix}storeengine_orders_meta";
263 $orders = "{$wpdb->prefix}storeengine_orders";
264 $wpdb->query( "ALTER TABLE $orders_meta DROP INDEX meta_key_value;" );
265 $wpdb->query( "ALTER TABLE $orders_meta ADD INDEX meta_key_value (meta_key(100), meta_value(73));" );
266 $wpdb->query( "ALTER TABLE $orders_meta ADD INDEX order_id_meta_key_meta_value (order_id, meta_key(100), meta_value(73));" );
267 $wpdb->query( "ALTER TABLE $orders DROP INDEX customer_id_billing_email;" );
268 $wpdb->query( "ALTER TABLE $orders ADD INDEX customer_id_billing_email (customer_id, billing_email(171));" );
269 $wpdb->query( "ALTER TABLE $orders DROP INDEX billing_email;" );
270 $wpdb->query( "ALTER TABLE $orders ADD INDEX INDEX billing_email (billing_email(191));" );
271 // End Missing migration for PR#803
272
273 // Alter meta key index length.
274 $wpdb->query( "ALTER TABLE $orders_meta DROP INDEX meta_key_index;" );
275 $wpdb->query( "ALTER TABLE $orders_meta ADD INDEX meta_key_index (meta_key_index(191));" );
276
277 // Add missing product-id index in price & variation table.
278 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_product_price ADD INDEX product_id (product_id);" );
279 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_product_variations ADD INDEX product_id (product_id);" );
280
281 // Rename variation_meta variation-id index.
282 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_product_variation_meta DROP INDEX order_id_index;" );
283 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_product_variation_meta ADD INDEX variation_id_index (variation_id);" );
284
285 // Update variation_meta index length
286 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_product_variation_meta DROP INDEX meta_key_index;" );
287 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_product_variation_meta ADD INDEX meta_key_index (meta_key(191));" );
288 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_product_variation_meta DROP INDEX meta_value_index;" );
289 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_product_variation_meta ADD INDEX meta_value_index (meta_value(191));" );
290
291 // Added missing zone_id & method_id key in variation_term_relations
292 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_shipping_zone_methods ADD INDEX zone_id (zone_id);" );
293 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_shipping_zone_methods ADD INDEX method_id (method_id);" );
294
295 // Added missing variation_id & term_id key in shipping_zone_methods
296 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_shipping_zone_methods ADD INDEX term_id (term_id);" );
297 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_shipping_zone_methods ADD INDEX variation_id (variation_id);" );
298
299 // Change api table last-access into timestamp & add created-at timestamp
300 $wpdb->query( "ALTER TABLE {$wpdb->prefix}storeengine_api_keys MODIFY `last_access` TIMESTAMP NULL DEFAULT NULL, ADD `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP;" );
301 }
302 }
303
304 public function migrate_1_5_0( $storeengine_version ) {
305 if ( version_compare( $storeengine_version, '1.5.0', '<' ) ) {
306 global $wpdb;
307 wp_cache_flush();
308 $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key = '_storeengine_memberships'" );
309 }
310 }
311
312 public function migrate_1_5_2( $storeengine_version ) {
313 if ( version_compare( $storeengine_version, '1.5.2', '<' ) ) {
314 if ( ! Helper::get_settings( 'product_archive_multi_price_display' ) ) {
315 Settings::save_settings();
316 Base::save_settings( [ 'product_archive_multi_price_display' => 'dropdown' ] );
317 }
318
319 // update email settings
320 $email_settings = get_option( 'storeengine_email_settings' );
321 if ( ! $email_settings ) {
322 return;
323 }
324 $email_settings = json_decode( $email_settings, true );
325 if ( ! is_array( $email_settings ) || isset( $email_settings['new_user_notification'] ) ) {
326 return;
327 }
328
329 $email_settings['new_user_notification'] = StoreEngine\Addons\Email\Admin\Settings::get_settings_default_data()['new_user_notification'];
330 update_option( 'storeengine_email_settings', wp_json_encode( $email_settings ) );
331 }
332 }
333
334 public function migrate_1_5_5( $storeengine_version ) {
335 if ( version_compare( $storeengine_version, '1.5.5', '<' ) ) {
336 global $wpdb;
337 // Migrate old meta-key to new meta-key.
338 $wpdb->query( "UPDATE $wpdb->postmeta SET meta_key = '_storeengine_coupon_customer_usage_limit' WHERE meta_key = '_storeengine_coupon_total_usage_limit';" );
339
340 // Remove unused metadata.
341 $wpdb->delete( "$wpdb->postmeta", [ 'meta_key' => '_storeengine_coupon_is_one_usage_per_user' ] );
342 $wpdb->delete( "$wpdb->postmeta", [ 'meta_key' => '_storeengine_coupon_is_total_usage_limit' ] );
343 $wpdb->delete( "$wpdb->postmeta", [ 'meta_key' => '_storeengine_per_user_coupon_usage_limit' ] );
344 $wpdb->delete( "$wpdb->postmeta", [ 'meta_key' => '_storeengine_coupon_customer_usage_limit_type' ] );
345 }
346 }
347
348 public function migrate_1_8_0( $storeengine_version ) {
349 if ( version_compare( $storeengine_version, '1.8.0', '<' ) ) {
350 global $wpdb;
351 $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}storeengine_log");
352 }
353 }
354
355 /**
356 * Create the storeengine_email_log table that the customer-communication
357 * audit (EmailLogger) writes into. dbDelta is idempotent so re-running on
358 * an existing install is safe; the version gate keeps it from firing on
359 * every page load.
360 */
361 public function migrate_1_10_0( $storeengine_version ) {
362 if ( version_compare( $storeengine_version, '1.10.0', '<' ) ) {
363 global $wpdb;
364 if ( ! function_exists( 'dbDelta' ) ) {
365 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
366 }
367
368 CreateEmailLog::up( $wpdb->prefix, $wpdb->get_charset_collate() );
369 }
370 }
371
372 /**
373 * `sticky_add_to_cart_mobile` → `sticky_add_to_cart`.
374 *
375 * The bar is no longer phone-only, so the `_mobile` suffix was a lie.
376 *
377 * Gated on its own flag rather than on `version_compare()`: the rename ships
378 * within 2.2.0, so any install already running a 2.2.0 build has that
379 * version stored and a version gate would skip it — silently dropping the
380 * merchant's choice back to the new key's default (enabled). It also has to
381 * run before run_migration()'s Settings::save_settings(), which merges
382 * defaults into the saved blob and would otherwise write the new key first
383 * and shadow the old value.
384 */
385 public function migrate_sticky_add_to_cart_key() {
386 if ( get_option( 'storeengine_sticky_atc_key_migrated' ) ) {
387 return;
388 }
389
390 $saved = Base::get_settings_saved_data();
391
392 // Only carry the value across when the merchant actually had the old key
393 // and has not already been moved onto the new one.
394 if ( array_key_exists( 'sticky_add_to_cart_mobile', $saved ) && ! array_key_exists( 'sticky_add_to_cart', $saved ) ) {
395 $saved['sticky_add_to_cart'] = (bool) $saved['sticky_add_to_cart_mobile'];
396 }
397
398 if ( array_key_exists( 'sticky_add_to_cart_mobile', $saved ) ) {
399 unset( $saved['sticky_add_to_cart_mobile'] );
400 update_option( STOREENGINE_SETTINGS_NAME, wp_json_encode( $saved ) );
401 }
402
403 update_option( 'storeengine_sticky_atc_key_migrated', 1, false );
404 }
405
406 /**
407 * Backfill the queryable coupon end-timestamp mirror.
408 *
409 * The admin coupon list filters "Expired" / "No end date" against
410 * `_storeengine_coupon_end_ts` (see API\Coupon), which is written on every
411 * save going forward. Existing coupons have never been re-saved, so seed the
412 * mirror once here. Flag-gated rather than version-gated so it still runs on
413 * installs already on this build (which would otherwise skip a version
414 * bump). `0` means "never expires".
415 */
416 public function migrate_coupon_end_timestamp() {
417 if ( get_option( 'storeengine_coupon_end_ts_migrated' ) ) {
418 return;
419 }
420
421 $coupon_ids = get_posts( [
422 'post_type' => Helper::COUPON_POST_TYPE,
423 'post_status' => 'any',
424 'posts_per_page' => -1,
425 'fields' => 'ids',
426 'no_found_rows' => true,
427 ] );
428
429 foreach ( $coupon_ids as $coupon_id ) {
430 $end_date_time = get_post_meta( $coupon_id, '_storeengine_coupon_end_date_time', true );
431 update_post_meta( $coupon_id, \StoreEngine\API\Coupon::END_TS_META, \StoreEngine\API\Coupon::compute_end_timestamp( $end_date_time ) );
432 }
433
434 update_option( 'storeengine_coupon_end_ts_migrated', 1, false );
435 }
436
437 // phpcs:enable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.SlowDBQuery.slow_db_query_meta_value, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.SlowDBQuery.slow_db_query_meta_key
438 }
439