* @link https://www.fireplugins.com * @copyright Copyright © 2026 FirePlugins All Rights Reserved * @license GNU GPLv3 or later */ namespace FireBox\Core\FB; if (!defined('ABSPATH')) { exit; // Exit if accessed directly. } /** * Handles migration of legacy meta data to new meta format */ class MetaMigrator { /** * Apply a transformation to meta data * * @param string $transformType The type of transformation to apply * @param string $key The meta key being transformed * @param array $new_meta Reference to the new meta data * @param array $legacy_meta The legacy meta data * @param array $defaults Default values * @param bool $valueExistsInNewMeta Whether the value already exists in new meta * @return void */ public function applyTransform($transformType, $key, &$new_meta, $legacy_meta, $defaults, $valueExistsInNewMeta) { switch ($transformType) { case 'migrate_unitvalue': $this->migrateUnitValue($key, $new_meta, $valueExistsInNewMeta); break; case 'border_migration': $this->migrateBorder($key, $new_meta, $legacy_meta, $valueExistsInNewMeta); break; case 'borderradius_migration': $this->migrateBorderRadius($key, $new_meta, $valueExistsInNewMeta); break; case 'dimension': $this->migrateDimension($key, $new_meta); break; case 'box_auto_close_migration': $this->migrateBoxAutoClose($key, $new_meta); break; case 'actions_migration': $this->migrateActions($key, $new_meta); break; case 'rules_migration': $this->migrateRules($key, $new_meta, $defaults); break; case 'animation_migration': $this->migrateAnimation($key, $new_meta, $valueExistsInNewMeta); break; case 'shadow_migration': $this->migrateShadow($key, $new_meta, $valueExistsInNewMeta); break; case 'floatingbutton_message_migration': $this->migrateFloatingButtonMessage($key, $new_meta, $defaults, $valueExistsInNewMeta); break; case 'overlay_enabled_migration': $this->migrateOverlayEnabled($key, $new_meta, $valueExistsInNewMeta); break; case 'overlayclick_migration': $this->migrateOverlayClick($key, $new_meta, $valueExistsInNewMeta); break; case 'overlayblurradius_migration': $this->migrateOverlayBlurRadius($key, $new_meta, $valueExistsInNewMeta); break; case 'triggermethod_migration': $this->migrateTriggerMethod($key, $new_meta, $legacy_meta, $valueExistsInNewMeta); break; case 'scroll_depth_migration': $this->migrateScrollDepth($key, $new_meta, $legacy_meta, $valueExistsInNewMeta); break; case 'firing_frequency_toggle_migration': $this->migrateFiringFrequencyToggle($key, $new_meta, $valueExistsInNewMeta); break; } } /** * Migrate unit values * * @param string $key * @param array $new_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateUnitValue($key, &$new_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta) { return; } if (!isset($new_meta[$key]) || !is_array($new_meta[$key])) { return; } foreach ($new_meta[$key] as $subkey => &$subvalue) { if (!isset($subvalue['value']) && !isset($subvalue['unit'])) { continue; } $value = isset($subvalue['value']) ? $subvalue['value'] : ''; if (!$value) { $subvalue = ''; // if empty height, set to auto if ($key === 'height') { $subvalue = 'auto'; } continue; } $unit = isset($subvalue['unit']) ? $subvalue['unit'] : ''; $subvalue = $value . $unit; } } /** * Migrate border settings * * @param string $key * @param array $new_meta * @param array $legacy_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateBorder($key, &$new_meta, $legacy_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta) { return; } if (!isset($legacy_meta['bordertype']) || $legacy_meta['bordertype'] === 'none') { return; } $borderStyle = isset($legacy_meta['bordertype']) ? $legacy_meta['bordertype'] : 'solid'; if (!in_array($borderStyle, ['solid', 'dashed', 'dotted'])) { $borderStyle = 'solid'; } $new_meta['border'] = [ 'width' => isset($legacy_meta['borderwidth']['value']) ? rtrim($legacy_meta['borderwidth']['value'], 'px') . 'px' : '', 'style' => $borderStyle, 'color' => isset($legacy_meta['bordercolor']) ? $legacy_meta['bordercolor'] : '' ]; } /** * Migrate border radius settings * * @param string $key * @param array $new_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateBorderRadius($key, &$new_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta) { return; } if (!isset($new_meta[$key]) || !is_array($new_meta[$key])) { return; } foreach ($new_meta[$key] as $breakpoint => &$values) { $unit = isset($values['unit']) ? $values['unit'] : 'px'; $values = [ 'topLeft' => isset($values['top_left']) ? $values['top_left'] . $unit : '', 'topRight' => isset($values['top_right']) ? $values['top_right'] . $unit : '', 'bottomLeft' => isset($values['bottom_left']) ? $values['bottom_left'] . $unit : '', 'bottomRight' => isset($values['bottom_right']) ? $values['bottom_right'] . $unit : '' ]; } } /** * Migrate dimension values * * @param string $key * @param array $new_meta * @return void */ public function migrateDimension($key, &$new_meta) { if (!is_array($new_meta[$key]) || !count($new_meta[$key])) { return; } foreach ($new_meta[$key] as $breakpoint => &$values) { $unit = isset($values['unit']) ? $values['unit'] : ''; if (!is_array($values) || !count($values)) { continue; } foreach ($values as $_prop => &$val) { if (in_array($_prop, ['top', 'right', 'bottom', 'left'])) { if ($val) { $val = $val . $unit; } } else { unset($values[$_prop]); } } } } /** * Migrate box auto close setting * * @param string $key * @param array $new_meta * @return void */ public function migrateBoxAutoClose($key, &$new_meta) { if (!is_array($new_meta[$key]) || !count($new_meta[$key])) { return; } $new_meta[$key] = ($new_meta[$key][0] === 'yes'); } /** * Migrate actions settings * * @param string $key * @param array $new_meta * @return void */ public function migrateActions($key, &$new_meta) { if (!is_array($new_meta[$key]) || !count($new_meta[$key])) { return; } $actions = array_values($new_meta[$key]); foreach ($actions as &$action) { $action['enabled'] = isset($action['enabled']) && $action['enabled'] == '1'; } $new_meta[$key] = $actions; } /** * Migrate rules settings * * @param string $key * @param array $new_meta * @param array $defaults * @return void */ public function migrateRules($key, &$new_meta, $defaults) { $rules = is_string($new_meta[$key]) ? json_decode($new_meta[$key], true) : $new_meta[$key]; if (empty($rules)) { // Set default rules if empty $new_meta[$key] = $defaults['rules']; return; } $rules = array_values($rules); foreach ($rules as &$set) { $set['enabled'] = isset($set['enabled']) && $set['enabled'] == '1'; if (!isset($set['rules']) || !is_array($set['rules']) || !count($set['rules'])) { continue; } foreach ($set['rules'] as &$rule) { $rule['enabled'] = isset($rule['enabled']) && $rule['enabled'] == '1'; // Fix name if (isset($rule['name'])) { $rule['name'] = str_replace('\\\\', '\\', $rule['name']); } // Fix "params.exclude_shipping_cost" if (isset($rule['params']['exclude_shipping_cost'])) { $rule['params']['exclude_shipping_cost'] = isset($rule['params']['exclude_shipping_cost']) && $rule['params']['exclude_shipping_cost'] == '1'; } // Migrate value from array of objects to array of values $migrateValueConditions = [ 'IP', 'Referrer', 'URL', 'Geo\City', 'Geo\Region' ]; if (isset($rule['name']) && in_array($rule['name'], $migrateValueConditions)) { if (isset($rule['value']) && is_array($rule['value'])) { $migratedValues = []; foreach ($rule['value'] as $item) { $old_value = isset($item['value']) ? $item['value'] : ''; if (!$old_value) { continue; } $migratedValues[] = $old_value; } $rule['value'] = $migratedValues; } } // Fix regex if (isset($rule['name']) && $rule['name'] === 'URL') { $rule['params']['regex'] = isset($rule['params']['regex']) && $rule['params']['regex'] == '1'; } // Fix quantity operators for WooCommerce Cart Contains Products rule if (isset($rule['name']) && in_array($rule['name'], ['WooCommerce\CartContainsProducts'])) { $value = isset($rule['value']) ? $rule['value'] : []; if (is_array($value)) { foreach ($value as &$item) { if (isset($item['quantity_operator'])) { if ($item['quantity_operator'] === 'less_than_equals') { $item['quantity_operator'] = 'less_than_or_equal_to'; } elseif ($item['quantity_operator'] === 'greater_than_equals') { $item['quantity_operator'] = 'greater_than_or_equal_to'; } } } } $rule['value'] = $value; } } } $new_meta[$key] = $rules; } /** * Migrate animation settings * * @param string $key * @param array $new_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateAnimation($key, &$new_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta) { return; } // Fade animations $fade_animations = [ 'transition.fadeIn' => 'fadeIn', 'transition.fadeOut' => 'fadeOut', 'transition.slideUpIn' => 'fadeInUp', 'transition.slideDownIn' => 'fadeInDown', 'transition.slideLeftIn' => 'fadeInLeft', 'transition.slideRightIn' => 'fadeInRight', 'transition.slideUpBigIn' => 'fadeInUpBig', 'transition.slideDownBigIn' => 'fadeInDownBig', 'transition.slideLeftBigIn' => 'fadeInLeftBig', 'transition.slideRightBigIn' => 'fadeInRightBig', 'transition.slideUpOut' => 'fadeOutUp', 'transition.slideDownOut' => 'fadeOutDown', 'transition.slideLeftOut' => 'fadeOutLeft', 'transition.slideRightOut' => 'fadeOutRight', 'transition.slideUpBigOut' => 'fadeOutUpBig', 'transition.slideDownBigOut' => 'fadeOutDownBig', 'transition.slideLeftBigOut' => 'fadeOutLeftBig', 'transition.slideRightBigOut' => 'fadeOutRightBig', ]; // Slide animations $slide_animations = [ 'slideDown' => 'slideInDown', 'slideUp' => 'slideOutUp', 'firebox.slideUpIn' => 'slideInUp', 'firebox.slideDownIn' => 'slideInDown', 'firebox.slideLeftIn' => 'slideInLeft', 'firebox.slideRightIn' => 'slideInRight', 'firebox.slideUpOut' => 'fadeOutDown', 'firebox.slideDownOut' => 'slideOutUp', 'firebox.slideLeftOut' => 'slideOutLeft', 'firebox.slideRightOut' => 'slideOutRight', 'transition.perspectiveUpIn' => 'slideInUp', 'transition.perspectiveDownIn' => 'slideInDown', 'transition.perspectiveLeftIn' => 'slideInLeft', 'transition.perspectiveRightIn' => 'slideInRight', 'transition.perspectiveUpOut' => 'slideOutUp', 'transition.perspectiveDownOut' => 'slideOutDown', 'transition.perspectiveLeftOut' => 'slideOutLeft', 'transition.perspectiveRightOut' => 'slideOutRight', ]; $animation_map = array_merge([ // Other animations 'callout.bounce' => 'bounce', 'callout.shake' => 'shakeX', 'callout.flash' => 'flash', 'callout.pulse' => 'pulse', 'callout.swing' => 'swing', 'callout.tada' => 'tada', 'transition.swoopIn' => 'zoomIn', 'transition.whirlIn' => 'zoomIn', 'transition.shrinkIn' => 'zoomIn', 'transition.expandIn' => 'zoomIn', 'transition.flipXIn' => 'flipInX', 'transition.flipYIn' => 'flipInY', 'transition.flipBounceXIn' => 'flipInX', 'transition.flipBounceYIn' => 'flipInY', 'transition.bounceIn' => 'bounceIn', 'transition.bounceUpIn' => 'bounceInUp', 'transition.bounceDownIn' => 'bounceInDown', 'transition.bounceLeftIn' => 'bounceInLeft', 'transition.bounceRightIn' => 'bounceInRight', 'transition.swoopOut' => 'zoomOut', 'transition.whirlOut' => 'zoomOut', 'transition.shrinkOut' => 'zoomOut', 'transition.expandOut' => 'zoomOut', 'transition.flipXOut' => 'flipOutX', 'transition.flipYOut' => 'flipOutY', 'transition.flipBounceXOut' => 'flipOutX', 'transition.flipBounceYOut' => 'flipOutY', 'transition.bounceOut' => 'bounceOut', 'transition.bounceUpOut' => 'bounceOutUp', 'transition.bounceDownOut' => 'bounceOutDown', 'transition.bounceLeftOut' => 'bounceOutLeft', 'transition.bounceRightOut' => 'bounceOutRight', ], $fade_animations, $slide_animations); // A campaign with no animation stored leaves this null, and PHP 8.5 deprecates // null as an array_key_exists() key. An empty key misses the map just as null did. $current_animation = is_scalar($new_meta[$key]) ? (string) $new_meta[$key] : ''; $new_meta[$key] = array_key_exists($current_animation, $animation_map) ? $animation_map[$current_animation] : $new_meta[$key]; // Validate page slide animation in/out if (isset($new_meta['mode']) && $new_meta['mode'] === 'pageslide') { $isFadeAnimation = in_array($new_meta[$key], $fade_animations); $isSlideAnimation = in_array($new_meta[$key], $slide_animations); if ($key === 'animationin') { if (!$isFadeAnimation && !$isSlideAnimation) { $new_meta[$key] = 'slideInDown'; } } else if ($key === 'animationout') { if (!$isFadeAnimation && !$isSlideAnimation) { $new_meta[$key] = 'slideOutUp'; } } } } /** * Migrate shadow settings * * @param string $key * @param array $new_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateShadow($key, &$new_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta || !isset($new_meta[$key])) { return; } $new_meta[$key] = $new_meta[$key] != '0'; } /** * Migrate floating button message settings * * @param string $key * @param array $new_meta * @param array $defaults * @param bool $valueExistsInNewMeta * @return void */ public function migrateFloatingButtonMessage($key, &$new_meta, $defaults, $valueExistsInNewMeta) { if ($valueExistsInNewMeta) { return; } if (isset($new_meta[$key]) && empty($new_meta[$key])) { $new_meta[$key] = $defaults['floatingbutton_message']; } else if (isset($new_meta[$key]['fontsize']) && is_numeric($new_meta[$key]['fontsize'])) { $new_meta[$key]['fontsize'] = $new_meta[$key]['fontsize'] . 'px'; } } /** * Migrate overlay enabled setting * * @param string $key * @param array $new_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateOverlayEnabled($key, &$new_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta || !isset($new_meta[$key])) { return; } $new_meta[$key] = ((int) $new_meta[$key]) == 1; } /** * Migrate overlay click setting * * @param string $key * @param array $new_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateOverlayClick($key, &$new_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta || !isset($new_meta[$key])) { return; } $overlayClick = (int) $new_meta[$key]; $new_meta[$key] = !empty($overlayClick) && $overlayClick != 0; } /** * Migrate overlay blur radius setting * * @param string $key * @param array $new_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateOverlayBlurRadius($key, &$new_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta) { return; } $overlayBlurRadius = isset($new_meta[$key]) ? (int) $new_meta[$key] : 0; if (empty($overlayBlurRadius) || $overlayBlurRadius == 0) { $new_meta[$key] = 0; } elseif ($overlayBlurRadius <= 10) { $new_meta[$key] = 2; } elseif ($overlayBlurRadius <= 30) { $new_meta[$key] = 14; } else { $new_meta[$key] = 45; } } /** * Migrate trigger method setting * * @param string $key * @param array $new_meta * @param array $legacy_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateTriggerMethod($key, &$new_meta, $legacy_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta) { return; } if (isset($new_meta[$key])) { if ($new_meta[$key] === 'pageready') { $new_meta[$key] = 'pageload'; $new_meta['early_trigger'] = true; } else if ($new_meta[$key] === 'floatingbutton') { $floating_button_position = isset($legacy_meta['floating_button_position']) ? $legacy_meta['floating_button_position'] : null; if ($floating_button_position) { $new_meta['position'] = $floating_button_position; } } } } /** * Migrate scroll depth setting * * @param string $key * @param array $new_meta * @param array $legacy_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateScrollDepth($key, &$new_meta, $legacy_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta) { return; } $scroll_amount = isset($legacy_meta['scroll_amount']) ? $legacy_meta['scroll_amount'] : 0; if ($scroll_amount) { $_value = isset($scroll_amount['value']) ? $scroll_amount['value'] : '80'; $_unit = isset($scroll_amount['unit']) ? $scroll_amount['unit'] : '%'; $new_meta[$key] = $_value . $_unit; } } /** * Migrate firing frequency toggle setting * * @param string $key * @param array $new_meta * @param bool $valueExistsInNewMeta * @return void */ public function migrateFiringFrequencyToggle($key, &$new_meta, $valueExistsInNewMeta) { if ($valueExistsInNewMeta || !isset($new_meta[$key])) { return; } $new_meta[$key] = $new_meta[$key] == '1'; } }