PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 3.1.10
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v3.1.10
3.1.13 3.1.12 3.1.11 3.1.10 3.1.9 3.1.8 3.1.7 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 All 122 releases
firebox / Inc / Core / Migrator.php

Migrator.php in FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment 3.1.10, at Inc/Core/Migrator.php

653 lines 14.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package FireBox
4 * @version 3.1.10 Free
5 *
6 * @author FirePlugins <info@fireplugins.com>
7 * @link https://www.fireplugins.com
8 * @copyright Copyright © 2026 FirePlugins All Rights Reserved
9 * @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
10 */
11
12 namespace FireBox\Core;
13
14 if (!defined('ABSPATH'))
15 {
16 exit; // Exit if accessed directly.
17 }
18
19 use FireBox\Core\Helpers\Form\Form;
20
21 class Migrator
22 {
23 /**
24 * The currently installed version.
25 *
26 * @var string
27 */
28 private $installedVersion;
29
30 /**
31 * Whether we run a migration.
32 *
33 * @var bool
34 */
35 private $run = false;
36
37 public function __construct($installedVersion = '')
38 {
39 $this->installedVersion = $installedVersion;
40 }
41
42 public function run()
43 {
44 if (!$this->installedVersion)
45 {
46 return;
47 }
48
49 $this->moveFireBoxDataToUploadsDirectory();
50 $this->installFormsTables();
51 $this->improveIndexes();
52 $this->updatePublishingRulesToConditionBuilder();
53 $this->updateEcommerceProductsInCartConditions();
54 $this->updateAnalyticsStoragePeriodSetting();
55 $this->update213version();
56 $this->addMissingCapabilities();
57
58
59 $this->addNewIndexes();
60 $this->addSubmissionsIndexes();
61 $this->preserveCampaignRoleAccess();
62
63 // Update firebox version
64 if ($this->run)
65 {
66 // Update version
67 update_option('firebox_version', FBOX_VERSION);
68 }
69 }
70
71 /**
72 * Moves plugin data to its own /wp-content/uploads/firebox folder.
73 *
74 * @since 1.1.10
75 *
76 * @return void
77 */
78 private function moveFireBoxDataToUploadsDirectory()
79 {
80 if (version_compare($this->installedVersion, '1.1.10', '>='))
81 {
82 return;
83 }
84
85 // Create dirs
86 \FireBox\Core\Helpers\Activation::createLibraryDirectories();
87
88 $this->run = true;
89 }
90
91 /**
92 * Creates the forms tables.
93 *
94 * @since 1.2.0
95 *
96 * @return void
97 */
98 private function installFormsTables()
99 {
100 if (version_compare($this->installedVersion, '1.2.0', '>='))
101 {
102 return;
103 }
104
105 if (!class_exists('Activation'))
106 {
107 require_once FBOX_PLUGIN_DIR . '/Inc/Framework/Inc/Admin/Includes/Activation.php';
108 }
109 $activation = new \Activation(firebox()->hook_data);
110 $activation->initTables();
111
112 $this->run = true;
113 }
114
115 /**
116 * Improves indexes on form and box logs tables.
117 *
118 * @since 1.2.4
119 *
120 * @return void
121 */
122 private function improveIndexes()
123 {
124 if (version_compare($this->installedVersion, '1.2.4', '>='))
125 {
126 return;
127 }
128
129 if (!$this->hasIndex('idx_box_id', 'firebox_logs'))
130 {
131 global $wpdb;
132 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_logs ADD INDEX `idx_box_id` (`box`, `id`)");
133 }
134
135 if (!$this->hasIndex('idx_meta_key', 'firebox_submission_meta'))
136 {
137 global $wpdb;
138 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_submission_meta ADD INDEX `idx_meta_key` (`meta_key`)");
139 }
140
141 $this->run = true;
142 }
143
144 /**
145 * Update old Publishing Rules to Condition Builder.
146 *
147 * @since 2.0.0
148 *
149 * @return void
150 */
151 private function updatePublishingRulesToConditionBuilder()
152 {
153 if (version_compare($this->installedVersion, '2.0.0', '>='))
154 {
155 return;
156 }
157
158 // Get all boxes, regardless of post status, thus we pass [] to ensure all popups are returned.
159 $boxes = \FireBox\Core\Helpers\BoxHelper::getAllBoxes([]);
160
161 if (!$boxes->posts)
162 {
163 $this->run = true;
164 return;
165 }
166
167 foreach ($boxes->posts as $box)
168 {
169 // Get post meta and add it to its object
170 $meta = \FireBox\Core\Helpers\BoxHelper::getMeta($box->ID);
171
172 // Skip popup that already has rules
173 if (isset($meta['rules']))
174 {
175 continue;
176 }
177
178 $box->params = new \FPFramework\Libs\Registry($meta);
179
180 // Pass only params
181 \FPFramework\Base\Conditions\Migrator::run($box->params);
182
183 // Update popup settings
184 update_post_meta($box->ID, 'fpframework_meta_settings', wp_slash(json_decode(wp_json_encode($box->params), true)));
185 }
186
187 $this->run = true;
188 }
189
190 /**
191 * Updates the following conditions to match the new condition settings.
192 *
193 * EDD\CartContainsProducts
194 * WooCommerce\CartContainsProducts
195 *
196 * @since 2.0.10
197 *
198 * @return void
199 */
200 private function updateEcommerceProductsInCartConditions()
201 {
202 if (version_compare($this->installedVersion, '2.0.10', '>='))
203 {
204 return;
205 }
206
207 // Get all boxes, regardless of post status, thus we pass [] to ensure all popups are returned.
208 $boxes = \FireBox\Core\Helpers\BoxHelper::getAllBoxes([]);
209
210 if (!$boxes->posts)
211 {
212 $this->run = true;
213 return;
214 }
215
216 foreach ($boxes->posts as $box)
217 {
218 $meta = \FireBox\Core\Helpers\BoxHelper::getMeta($box->ID);
219
220 if (!isset($meta['rules']) || !is_array($meta['rules']))
221 {
222 continue;
223 }
224
225 $allowed_rules = [
226 'EDD\CartContainsProducts',
227 'WooCommerce\CartContainsProducts'
228 ];
229
230 $changed = false;
231
232 foreach ($meta['rules'] as &$ruleset)
233 {
234 if (!isset($ruleset['rules']))
235 {
236 continue;
237 }
238
239 foreach ($ruleset['rules'] as &$rule)
240 {
241 if (!isset($rule['name']))
242 {
243 continue;
244 }
245
246 if (!in_array($rule['name'], $allowed_rules))
247 {
248 continue;
249 }
250
251 if (!isset($rule['value']))
252 {
253 continue;
254 }
255
256 if (!is_array($rule['value']))
257 {
258 continue;
259 }
260
261 if (!count($rule['value']))
262 {
263 continue;
264 }
265
266 $changed = true;
267
268 $rule['value'] = array_map(function($id) {
269 // Old value wasn't an array, skip if new value was found
270 if (is_array($id))
271 {
272 return $id;
273 }
274
275 return [
276 'value' => $id,
277 'quantity_operator' => 'any',
278 'quantity_value1' => '1',
279 'quantity_value2' => '1'
280 ];
281 }, $rule['value']);
282 }
283 }
284
285 if (!$changed)
286 {
287 continue;
288 }
289
290 update_post_meta($box->ID, 'fpframework_meta_settings', wp_slash(json_decode(wp_json_encode($meta), true)));
291 }
292
293 $this->run = true;
294 }
295
296 /**
297 * Updates the Analytics Storage Period setting.
298 *
299 * @since 2.1.1
300 *
301 * @return void
302 */
303 public function updateAnalyticsStoragePeriodSetting()
304 {
305 if (version_compare($this->installedVersion, '2.1.1', '>='))
306 {
307 return;
308 }
309
310 $params = get_option('firebox_settings', []);
311
312 $statsdays = isset($params['statsdays']) ? $params['statsdays'] : 730;
313 $statsdays_custom = isset($params['statsdays_custom']) ? $params['statsdays_custom'] : false;
314
315 if ($statsdays === 'custom')
316 {
317 $statsdays = $statsdays_custom;
318 }
319
320 $statsdays = (int) $statsdays;
321
322 if ($statsdays <= 365)
323 {
324 $statsdays = 365;
325 }
326 else if ($statsdays <= 365 * 2)
327 {
328 $statsdays = 365 * 2;
329 }
330 else
331 {
332 $statsdays = 365 * 5;
333 }
334
335 $params['statsdays'] = $statsdays;
336
337 update_option('firebox_settings', $params);
338
339 $this->run = true;
340 }
341
342 public function update213version()
343 {
344 $this->updateLogsDetailsParamsColumn();
345 $this->moveFormConversionsToLogsDetailsTable();
346 }
347
348 /**
349 * Adds a new column called event_source after event column.
350 * Renames the params column in the logs details table to event_label.
351 *
352 * @since 2.1.4
353 *
354 * @return void
355 */
356 public function updateLogsDetailsParamsColumn()
357 {
358 if (version_compare($this->installedVersion, '2.1.4', '>='))
359 {
360 return;
361 }
362
363 global $wpdb;
364
365 // If has column params
366 $row = $wpdb->get_results("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = '{$wpdb->dbname}' AND table_name = '{$wpdb->prefix}firebox_logs_details' AND column_name = 'params'");
367 if (!$row)
368 {
369 $this->run = true;
370 return;
371 }
372
373 // Add column "event_source" after "event"
374 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_logs_details ADD COLUMN `event_source` varchar(200) NOT NULL AFTER `event`");
375
376 // Rename params to event_label
377 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_logs_details CHANGE COLUMN `params` `event_label` text NOT NULL");
378
379 $this->run = true;
380 }
381
382 /**
383 * Moves form conversions from submission meta table to logs details table.
384 *
385 * @since 2.1.4
386 *
387 * @return void
388 */
389 private function moveFormConversionsToLogsDetailsTable()
390 {
391 if (version_compare($this->installedVersion, '2.1.4', '>='))
392 {
393 return;
394 }
395
396 global $wpdb;
397
398 // Get all submission meta data
399 $boxlogids = firebox()->tables->submissionmeta->getResults([
400 'select' => [
401 "{$wpdb->prefix}firebox_submission_meta.id",
402 'l.box',
403 's.form_id',
404 "{$wpdb->prefix}firebox_submission_meta.meta_value",
405 "{$wpdb->prefix}firebox_submission_meta.created_at"
406 ],
407 'where' => [
408 'meta_key' => " = '" . absint('box_log_id') . "'"
409 ],
410 'join' => [
411 "LEFT JOIN" => " {$wpdb->prefix}firebox_submissions as s ON s.id = submission_id LEFT JOIN {$wpdb->prefix}firebox_logs as l ON l.id = meta_value",
412 ]
413 ]);
414
415 if (!$boxlogids)
416 {
417 $this->run = true;
418 return;
419 }
420
421 global $wpdb;
422
423 // Migrate to the box logs details table
424 foreach ($boxlogids as $row)
425 {
426 if (!$row->box)
427 {
428 continue;
429 }
430
431 $form = Form::getFormByID($row->form_id);
432
433 $data = [
434 'log_id' => $row->meta_value,
435 'event' => 'conversion',
436 'event_source' => 'form',
437 'event_label' => isset($form['block']['attrs']['formName']) ? $form['block']['attrs']['formName'] : 'FireBox #' . $row->box . ' Form',
438 'date' => $row->created_at
439 ];
440
441 firebox()->tables->boxlogdetails->insert($data);
442 }
443
444 // Delete all submission_meta rows
445 $ids = implode(',', array_map('intval', array_column($boxlogids, 'id')));
446 $sm_table_name = firebox()->tables->submissionmeta->getFullTableName();
447 $wpdb->query("DELETE FROM $sm_table_name WHERE id IN($ids)");// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
448
449 $this->run = true;
450 }
451
452 public function addMissingCapabilities()
453 {
454 if (version_compare($this->installedVersion, '2.1.15', '>='))
455 {
456 return;
457 }
458
459 $cap_name = 'read_fireboxes';
460
461 $admin = get_role('administrator');
462
463 if ($admin)
464 {
465 if ($admin->has_cap($cap_name))
466 {
467 return true;
468 }
469
470 $admin->add_cap($cap_name);
471 }
472 else
473 {
474 $roles = get_editable_roles();
475
476 foreach ($roles as $role_name => $data)
477 {
478 if (isset($data['capabilities']['manage_options']) && $data['capabilities']['manage_options'])
479 {
480 $role = get_role($role_name);
481
482 if (!$role)
483 {
484 continue;
485 }
486
487 if ($role->has_cap($cap_name))
488 {
489 continue;
490 }
491
492 $role->add_cap($cap_name);
493 }
494 }
495 }
496
497 wp_get_current_user()->get_role_caps();
498
499 return true;
500 }
501
502
503
504
505
506 /**
507 * Adds new indexes to the logs details table.
508 *
509 * @since 3.1.0
510 *
511 * @return void
512 */
513 private function addNewIndexes()
514 {
515 if (version_compare($this->installedVersion, '3.1.0', '>='))
516 {
517 return;
518 }
519
520 if (!$this->hasIndex('idx_firebox_logs_details_event_log', 'firebox_logs_details'))
521 {
522 global $wpdb;
523 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_logs_details ADD INDEX `idx_firebox_logs_details_event_log` (`event`, `log_id`)");
524 }
525 if (!$this->hasIndex('idx_firebox_logs_details_event_source_log', 'firebox_logs_details'))
526 {
527 global $wpdb;
528 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_logs_details ADD INDEX `idx_firebox_logs_details_event_source_log` (`event_source`, `log_id`)");
529 }
530 if (!$this->hasIndex('idx_firebox_logs_details_event_date', 'firebox_logs_details'))
531 {
532 global $wpdb;
533 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_logs_details ADD INDEX `idx_firebox_logs_details_event_date` (`event`, `date`)");
534 }
535 if (!$this->hasIndex('idx_firebox_logs_details_event_source_event_log', 'firebox_logs_details'))
536 {
537 global $wpdb;
538 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_logs_details ADD INDEX `idx_firebox_logs_details_event_source_event_log` (`event_source`, `event`, `log_id`)");
539 }
540 if (!$this->hasIndex('idx_firebox_logs_details_event_source_event_date', 'firebox_logs_details'))
541 {
542 global $wpdb;
543 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_logs_details ADD INDEX `idx_firebox_logs_details_event_source_event_date` (`event_source`, `event`, `date`)");
544 }
545
546 $this->run = true;
547 }
548
549 /**
550 * Checks whether an index exists in a table.
551 *
552 * @param string $index
553 * @param string $table
554 *
555 * @return bool
556 */
557 /**
558 * Adds missing indexes on the submissions tables to speed up
559 * form_id/state lookups and submission_id meta lookups.
560 *
561 * @since 3.1.9
562 *
563 * @return void
564 */
565 private function addSubmissionsIndexes()
566 {
567 if (version_compare($this->installedVersion, '3.1.9', '>='))
568 {
569 return;
570 }
571
572 if (!$this->hasIndex('idx_form_id_state', 'firebox_submissions'))
573 {
574 global $wpdb;
575 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_submissions ADD INDEX `idx_form_id_state` (`form_id`, `state`)");
576 }
577
578 if (!$this->hasIndex('idx_submission_id', 'firebox_submission_meta'))
579 {
580 global $wpdb;
581 $wpdb->query("ALTER TABLE {$wpdb->prefix}firebox_submission_meta ADD INDEX `idx_submission_id` (`submission_id`)");
582 }
583
584 $this->run = true;
585 }
586
587 /**
588 * Keeps existing roles' campaign access working after the campaign post type moved
589 * from core's "post" capabilities to FireBox's own.
590 *
591 * Before this change anyone with edit_posts could edit campaigns. Granting each role
592 * the FireBox capability matching the core capability it already holds preserves
593 * exactly who could do what, so upgrading changes nobody's access.
594 *
595 * Note this deliberately does not grant read_fireboxes: that gates the FireBox admin
596 * screens, was never derived from the post type, and granting it here would hand
597 * roles access they did not previously have.
598 *
599 * Site owners who want to tighten campaign access can now remove these capabilities
600 * and have it actually take effect, which was not previously the case.
601 *
602 * @since 3.1.10
603 *
604 * @return void
605 */
606 private function preserveCampaignRoleAccess()
607 {
608 if (version_compare($this->installedVersion, '3.1.10', '>='))
609 {
610 return;
611 }
612
613 $map = \FireBox\Core\Admin\Includes\Cpts\Firebox::getPostTypeCapabilities();
614
615 // Only the primitive capabilities are assigned to roles.
616 unset($map['edit_post'], $map['read_post'], $map['delete_post'], $map['create_posts']);
617
618 // wp_roles() rather than get_editable_roles(): the latter is admin-only and
619 // undefined on the frontend requests this migration also runs on.
620 foreach (wp_roles()->role_objects as $role)
621 {
622 foreach ($map as $core_cap => $firebox_cap)
623 {
624 if (!empty($role->capabilities[$core_cap]))
625 {
626 $role->add_cap($firebox_cap);
627 }
628 }
629 }
630
631 $this->run = true;
632 }
633
634 private function hasIndex($index = '', $table = '')
635 {
636 if (empty($index) || empty($table))
637 {
638 return;
639 }
640
641 global $wpdb;
642
643 $existing = $wpdb->get_row("SHOW CREATE TABLE `{$wpdb->prefix}{$table}`", ARRAY_N);// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
644
645 if (isset($existing[1]) && strpos(strtolower($existing[1]), 'key `' . $index . '` (') !== false)
646 {
647 return true;
648 }
649
650 return false;
651 }
652 }
653