PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.8.14.1
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.8.14.1
7.8.14 7.8.14.1 7.8.13 7.8.13.1 trunk 3.0 3.1 3.1.1 3.1.2 3.1.3 3.1.4 4.3.2 4.4.4 4.4.5 4.4.5.1 4.4.5.4 4.6 4.6.1.1 4.6.1.4 4.7.0.2 4.7.0.3 4.7.0.7 4.7.0.9 4.7.1.0 4.7.1.1 4.8.0.0 5.0.0 5.0.1 5.0.1.1 5.0.1.2 5.1 5.1.1 5.1.2 5.1.3 5.1.3.1 5.1.3.2 5.1.4 5.1.5 6.0 6.0.1 6.0.2 6.0.3 6.0.4.2 6.0.5 6.0.6.1 6.0.7 6.0.8.1 6.0.9 7.0.0.1 7.0.2 7.0.3 7.0.4 7.1.0 7.1.1 7.2.0 7.2.1 7.3.0 7.3.1 7.3.3 7.3.5 7.3.6 7.3.7 7.4.0 7.4.1 7.4.11 7.4.13 7.4.13.1 7.4.2 7.4.3 7.4.4 7.4.5 7.4.5.1 7.4.5.2 7.4.6 7.4.7 7.5.0 7.6.0 7.6.1 7.6.3 7.6.4 7.6.6 7.7.0 7.7.1 7.8.0 7.8.1 7.8.10 7.8.10.1 7.8.10.2 7.8.11 7.8.12 7.8.12.1 7.8.2 7.8.3 7.8.4 7.8.5 7.8.6 7.8.7 7.8.8 7.8.9 7.8.9.1 7.8.9.2 7.8.9.3
wordpress-popup / inc / update / class-hustle-410-migration.php
wordpress-popup / inc / update Last commit date
class-hustle-410-migration.php 5 months ago class-hustle-430-migration.php 3 years ago class-hustle-441-migration.php 3 years ago
class-hustle-410-migration.php
465 lines
1 <?php
2 /**
3 * File for Hustle_410_Migration class.
4 *
5 * @package Hustle
6 * @since 4.1.0
7 */
8
9 /**
10 * Class Hustle_410_Migration.
11 *
12 * This class handles the migration when going from 4.0.x to 4.1.x
13 * We adjusted the modules' visibility conditions, and we need the
14 * conditions from 4.0.x to remain compatible to how we're handling them from 4.1.x on.
15 * Note this won't make the modules behave as they used to. This just makes the conditions that
16 * changed, such as Source of Arrival, Posts/Pages/Tags/Categories all/none options, and so on,
17 * compatible with their expected values in 4.1.x.
18 *
19 * @since 4.1.0
20 */
21 class Hustle_410_Migration {
22
23 /**
24 * Flag name to mark the migration as "done".
25 *
26 * @since 4.1.0
27 */
28 const MIGRATION_FLAG = 'hustle_40_migrated';
29
30 /**
31 * Meta key for the visibility backup.
32 *
33 * @since 4.1.0
34 */
35 const VISIBILITY_BACKUP_META = 'visibility_backup_40x';
36
37 /**
38 * Instance of the wpdb class.
39 *
40 * @since 4.1.0
41 * @var object
42 */
43 private $wpdb;
44
45 /**
46 * Array of metas to be inserted.
47 *
48 * @since 4.1.0
49 * @var array
50 */
51 private $backup_metas = array();
52
53 /**
54 * Hustle_401_Migration class constructor.
55 */
56 public function __construct() {
57
58 global $wpdb;
59 $this->wpdb = $wpdb;
60
61 if ( $this->is_migrating() ) {
62 add_action( 'init', array( $this, 'do_migration' ) );
63 }
64 }
65
66 /**
67 * Checks whether we should run da migration.
68 *
69 * @since 4.1.0
70 *
71 * @return bool
72 */
73 private function is_migrating() {
74
75 // If migration is being forced, do it.
76 if ( filter_input( INPUT_GET, 'run_41_migration', FILTER_VALIDATE_BOOLEAN ) ) {
77 return true;
78 }
79
80 // If migration was already done, skip.
81 if ( Hustle_Migration::is_migrated( self::MIGRATION_FLAG ) ) {
82 $prev_version = Hustle_Migration::get_previous_installed_version();
83 if ( $prev_version && version_compare( $prev_version, '4.1', '>=' ) ) {
84 Hustle_Notifications::add_dismissed_notification( '41_visibility_behavior_update' );
85 }
86 return false;
87 }
88
89 return ! self::is_fresh();
90 }
91
92 /**
93 * Checks if it's a fresh 4.1 installation or not
94 *
95 * @since 4.1.0
96 *
97 * @return bool
98 */
99 private static function is_fresh() {
100 $is_fresh = Hustle_Db::$is_fresh_install;
101
102 if ( $is_fresh ) {
103 Hustle_Migration::migration_passed( self::MIGRATION_FLAG );
104 }
105
106 return $is_fresh;
107 }
108
109 /**
110 * Does the migration from 4.0.x to 4.1.x.
111 *
112 * @since 4.1.0
113 */
114 public function do_migration() {
115
116 $limit = apply_filters( 'hustle_40_migration_limit', 100 );
117
118 // Restore the 4.0.x metas if they exist.
119 // Avoid duplicated backup metas and running migration on already migrated settings.
120 if ( $this->is_backup_created() ) {
121 $this->restore( false );
122 }
123
124 do {
125 $offset = get_option( 'hustle_40_migration_offset', 0 );
126 $m2_modules = get_option( 'hustle_notice_stop_support_m2', array() );
127 $conditions = $this->get_all_hustle_module_conditions( $limit, $offset );
128
129 foreach ( $conditions as $meta ) {
130
131 // Let's keep aside this meta to save them all together at the end.
132 $this->queue_meta_to_backup( $meta );
133
134 $meta_id = $meta->meta_id;
135 $value = json_decode( $meta->meta_value, true );
136
137 if ( empty( $value['conditions'] ) || ! is_array( $value['conditions'] ) || 1 > count( $value['conditions'] ) ) {
138
139 $group_id = substr( md5( wp_rand() ), 0, 10 );
140
141 $value['conditions'] = array(
142 $group_id => array(
143 'filter_type' => 'all',
144 'group_id' => $group_id,
145 ),
146 );
147 }
148
149 foreach ( $value['conditions'] as $group_id => $conds ) {
150
151 if ( ! empty( $conds['ms_membership'] ) || ! empty( $conds['ms_membership-n'] ) ) {
152 $m2_modules[] = $meta_id;
153 }
154
155 unset( $conds['group_id'], $conds['filter_type'] );
156 $count_conds = count( $conds );
157
158 if ( ! $count_conds || ! empty( $conds['page_404'] ) ) {
159
160 // Hide on 404 page according old behavior.
161 $filter_type = ! $count_conds ? 'except' : 'only';
162 $conds['wp_conditions'] = array(
163 'wp_conditions' => array( 'is_404' ),
164 'filter_type' => $filter_type,
165 );
166
167 // By default, we start showing modules on 404 page.
168 unset( $conds['page_404'] );
169 }
170
171 if ( ! empty( $conds['source_of_arrival']['source_external'] )
172 && 'true' === $conds['source_of_arrival']['source_external'] ) {
173 $conds['source_of_arrival']['source_direct'] = 'true';
174 }
175
176 // Remove 'all' values.
177 $post_types = Opt_In_Utils::get_post_types();
178 $cpts = array_keys( $post_types );
179 $types = array_merge( array( 'posts', 'pages', 'tags', 'categories' ), $cpts );
180
181 foreach ( $types as $type ) {
182 if (
183 (
184 ! empty( $conds[ $type ][ $type ] ) &&
185 in_array( 'all', $conds[ $type ][ $type ], true )
186 ) ||
187 (
188 ! empty( $conds[ $type ]['selected_cpts'] ) &&
189 in_array( 'all', $conds[ $type ]['selected_cpts'], true )
190 )
191 ) {
192 unset( $conds[ $type ][ $type ], $conds[ $type ]['selected_cpts'] );
193 $conds[ $type ]['filter_type'] = empty( $conds[ $type ]['filter_type'] ) || 'only' !== $conds[ $type ]['filter_type'] ? 'only' : 'except';
194 }
195 }
196
197 // Transform condition rules according new logic.
198 $and_rules = array(
199 'visitor_logged_in_status',
200 'visitor_device',
201 'from_referrer',
202 'source_of_arrival',
203 'on_url',
204 'visitor_commented',
205 'visitor_country',
206 'shown_less_than',
207 );
208
209 $or_rules = array_diff_key( $conds, array_flip( $and_rules ) );
210
211 // Get "AND" conditions.
212 $and_conds = array_intersect_key( $conds, array_flip( $and_rules ) );
213
214 if ( isset( $or_rules['pages'] ) && 1 < count( $or_rules ) ) {
215 $this->add_new_group(
216 $value,
217 array_merge( $and_conds, array( 'pages' => $conds['pages'] ) )
218 );
219 unset( $conds['pages'] );
220 unset( $or_rules['pages'] );
221 }
222
223 $post_group = array_intersect_key( $or_rules, array_flip( array( 'posts', 'tags', 'categories' ) ) );
224 if ( ! empty( $post_group ) && count( $post_group ) < count( $or_rules ) ) {
225 $this->add_new_group( $value, array_merge( $and_conds, $post_group ) );
226
227 unset( $conds['posts'], $conds['tags'], $conds['categories'] );
228 unset( $or_rules['posts'], $or_rules['tags'], $or_rules['categories'] );
229 }
230
231 foreach ( $or_rules as $name => $args ) {
232 if ( 2 > count( $or_rules ) ) {
233 break;
234 }
235 $this->add_new_group(
236 $value,
237 array_merge( $and_conds, array( $name => $args ) )
238 );
239 unset( $conds[ $name ] );
240 unset( $or_rules[ $name ] );
241 }
242
243 // Add new show/hide option.
244 $conds['show_or_hide_conditions'] = 'show';
245 $conds['filter_type'] = 'all';
246 $conds['group_id'] = $group_id;
247 $value['conditions'][ $group_id ] = $conds;
248 }
249
250 // Save transformed conditions.
251 $this->wpdb->update(
252 Hustle_Db::modules_meta_table(),
253 array( 'meta_value' => wp_json_encode( $value ) ), // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
254 array( 'meta_id' => $meta_id )
255 );
256
257 wp_cache_delete( $meta->module_id, 'hustle_module_meta' );
258 }
259
260 $count_conditions = count( $conditions );
261 $offset += $limit;
262
263 update_option( 'hustle_40_migration_offset', $offset );
264 update_option( 'hustle_notice_stop_support_m2', $m2_modules );
265
266 // Store the backup metas.
267 $this->insert_backup_metas();
268
269 } while ( $count_conditions === $limit );
270
271 Hustle_Migration::migration_passed( self::MIGRATION_FLAG );
272 delete_option( 'hustle_40_migration_offset' );
273 }
274
275 /**
276 * Gets all the stored visibility metas.
277 *
278 * @since 4.1.0
279 *
280 * @param int $limit Query limit.
281 * @param int $offset Query offset.
282 * @return array
283 */
284 private function get_all_hustle_module_conditions( $limit, $offset ) {
285
286 $modules_table = Hustle_Db::modules_meta_table();
287
288 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
289 $query = $this->wpdb->prepare( "SELECT meta_id, module_id, meta_value FROM {$modules_table} WHERE meta_key = 'visibility' LIMIT %d OFFSET %d", intval( $limit ), intval( $offset ) );
290 $results = $this->wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
291
292 return $results;
293 }
294
295 /**
296 * Adds a new group within the passed visibility settings.
297 *
298 * @since 4.1.0
299 *
300 * @param array $conditions Reference to the settings to which this group will be added.
301 * @param array $args Group's properties and conditions.
302 */
303 private function add_new_group( &$conditions, $args ) {
304
305 $new_group_id = substr( md5( wp_rand() ), 0, 10 );
306
307 if ( ! isset( $args['filter_type'] ) ) {
308 $args['filter_type'] = 'all';
309 $args['group_id'] = $new_group_id;
310 }
311
312 // Add new show/hide option.
313 $args['show_or_hide_conditions'] = 'show';
314
315 $conditions['conditions'][ $new_group_id ] = $args;
316 }
317
318 /**
319 * Checks if there's any backup meta already created.
320 *
321 * These should be deleted when rolling back, so if a single one exists,
322 * it's likely that we have them all and creating new ones isn't needed.
323 *
324 * @since 4.1.0
325 *
326 * @return bool
327 */
328 public function is_backup_created() {
329
330 $modules_table = Hustle_Db::modules_meta_table();
331
332 $query = $this->wpdb->prepare( "SELECT meta_id FROM {$modules_table} WHERE meta_key = %s LIMIT 1", self::VISIBILITY_BACKUP_META ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
333
334 $results = $this->wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
335
336 $is_backup_created = 0 < count( $results );
337
338 return $is_backup_created;
339 }
340
341 /**
342 * Adds the old meta to be inserted later on.
343 *
344 * Store the old 4.0.x meta formatted for INSERT in the class property,
345 * to be saved in the db later on. Used when looping through the metas.
346 *
347 * @since 4.1.0
348 *
349 * @param object $meta The original 4.0.x visibility meta retrieved from the db.
350 */
351 private function queue_meta_to_backup( $meta ) {
352
353 // Format this ready for IMPORT.
354 $row = $this->wpdb->prepare( '(%d, %s, %s)', $meta->module_id, self::VISIBILITY_BACKUP_META, $meta->meta_value );
355
356 $this->backup_metas[] = $row;
357 }
358
359 /**
360 * Inserts the backup visibility metas into the db.
361 *
362 * @since 4.1.0
363 */
364 private function insert_backup_metas() {
365
366 // Skip if there isn't any meta queued.
367 if ( empty( $this->backup_metas ) ) {
368 return;
369 }
370
371 $modules_meta_table = Hustle_Db::modules_meta_table();
372 $backup_values = implode( ', ', $this->backup_metas );
373
374 // Build the query with the already queued metas.
375 $sql = "INSERT INTO {$modules_meta_table} (module_id, meta_key, meta_value) VALUES {$backup_values}";
376
377 // Do the insert.
378 $this->wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
379
380 // Empty the property.
381 $this->backup_metas = array();
382 }
383
384 /*
385 ---------- Deactivation stuff ----------
386 */
387
388 /**
389 * Deletes the 4.1.x visibility metas and restore the ones for 4.0.x.
390 *
391 * This will allow admins to find their old visibility settings
392 * when they activate 4.0.x again.
393 *
394 * @since 4.1.0
395 *
396 * @param bool $check_if_exists Skip check for existing backup. False only if already checked.
397 *
398 * @throws Exception When there's no data to restore or the restore failed.
399 * @return bool
400 */
401 private function restore( $check_if_exists = true ) {
402
403 try {
404
405 // If there's nothing to restore, abort.
406 if ( $check_if_exists && ! $this->is_backup_created() ) {
407 throw new Exception( __( "There's no backup to restore.", 'hustle' ) );
408 }
409
410 $modules_meta_table = Hustle_Db::modules_meta_table();
411
412 // Get the meta id and module id of the modules with backups.
413 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
414 $backup_sql = $this->wpdb->prepare( "SELECT meta_id, module_id FROM {$modules_meta_table} WHERE meta_key = %s", self::VISIBILITY_BACKUP_META );
415 $backup_result = $this->wpdb->get_results( $backup_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
416
417 $backup_modules_id = array_column( $backup_result, 'module_id' );
418
419 if ( empty( $backup_modules_id ) ) {
420 throw new Exception( __( "There's no backup to restore.", 'hustle' ) );
421 }
422
423 // Delete the visibility conditions created for 4.1.x migration.
424 $modules_id_holder = implode( ', ', array_fill( 0, count( $backup_modules_id ), '%s' ) );
425
426 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
427 $delete_sql = $this->wpdb->prepare( "DELETE FROM {$modules_meta_table} WHERE module_id IN ($modules_id_holder) AND meta_key = 'visibility'", $backup_modules_id );
428 $deleted = $this->wpdb->query( $delete_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
429
430 // Check if the metas were successfully deleted.
431 if ( false === $deleted ) {
432 throw new Exception( __( "The 4.1.x visibility metas couldn't be deleted.", 'hustle' ) );
433 }
434
435 // Restore the visibility conditions created in 4.0.x.
436 $backup_metas_id = array_column( $backup_result, 'meta_id' );
437
438 $metas_id_holder = implode( ', ', array_fill( 0, count( $backup_metas_id ), '%s' ) );
439
440 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
441 $rename_sql = $this->wpdb->prepare( "UPDATE {$modules_meta_table} SET meta_key = 'visibility' WHERE meta_id IN ($metas_id_holder)", $backup_metas_id );
442 $renamed = $this->wpdb->query( $rename_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
443
444 // Check if the old metas were successfully restored.
445 if ( false === $renamed ) {
446 throw new Exception( __( "The 4.0.x visibility metas couldn't be restored.", 'hustle' ) );
447 }
448 } catch ( Exception $e ) {
449
450 $message = Opt_In_Utils::maybe_log( __METHOD__, $e->getMessage() );
451 return false;
452 }
453
454 // Clear caches.
455 foreach ( $backup_modules_id as $id ) {
456 wp_cache_delete( $id, 'hustle_module_meta' );
457 }
458
459 // Reset the migration flag so migration runs next time 4.1.x is enabled.
460 Hustle_Migration::remove_migration_passed_flag( self::MIGRATION_FLAG );
461
462 return true;
463 }
464 }
465