balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

upgrades.php (26455B)


      1 <?php
      2 namespace Elementor\Core\Upgrade;
      3 
      4 use Elementor\Core\Breakpoints\Manager as Breakpoints_Manager;
      5 use Elementor\Core\Experiments\Manager as Experiments_Manager;
      6 use Elementor\Core\Settings\Manager as SettingsManager;
      7 use Elementor\Core\Settings\Page\Manager as SettingsPageManager;
      8 use Elementor\Icons_Manager;
      9 use Elementor\Modules\Usage\Module;
     10 use Elementor\Plugin;
     11 use Elementor\Utils;
     12 
     13 if ( ! defined( 'ABSPATH' ) ) {
     14 	exit; // Exit if accessed directly.
     15 }
     16 
     17 /**
     18  * Elementor upgrades.
     19  *
     20  * Elementor upgrades handler class is responsible for updating different
     21  * Elementor versions.
     22  *
     23  * @since 1.0.0
     24  */
     25 class Upgrades {
     26 
     27 	public static function _on_each_version( $updater ) {
     28 		self::recalc_usage_data( $updater );
     29 
     30 		$uploads_manager = Plugin::$instance->uploads_manager;
     31 
     32 		$temp_dir = $uploads_manager->get_temp_dir();
     33 
     34 		if ( file_exists( $temp_dir ) ) {
     35 			$uploads_manager->remove_file_or_dir( $temp_dir );
     36 		}
     37 	}
     38 
     39 	/**
     40 	 * Upgrade Elementor 0.3.2
     41 	 *
     42 	 * Change the image widget link URL, setting is to `custom` link.
     43 	 *
     44 	 * @since 2.0.0
     45 	 * @static
     46 	 * @access public
     47 	 */
     48 	public static function _v_0_3_2() {
     49 		global $wpdb;
     50 
     51 		$post_ids = $wpdb->get_col(
     52 			'SELECT `post_id` FROM `' . $wpdb->postmeta . '`
     53 					WHERE `meta_key` = \'_elementor_version\'
     54 						AND `meta_value` = \'0.1\';'
     55 		);
     56 
     57 		if ( empty( $post_ids ) ) {
     58 			return;
     59 		}
     60 
     61 		foreach ( $post_ids as $post_id ) {
     62 			$document = Plugin::$instance->documents->get( $post_id );
     63 
     64 			if ( $document ) {
     65 				$data = $document->get_elements_data();
     66 			}
     67 
     68 			if ( empty( $data ) ) {
     69 				continue;
     70 			}
     71 
     72 			$data = Plugin::$instance->db->iterate_data( $data, function( $element ) {
     73 				if ( empty( $element['widgetType'] ) || 'image' !== $element['widgetType'] ) {
     74 					return $element;
     75 				}
     76 
     77 				if ( ! empty( $element['settings']['link']['url'] ) && ! isset( $element['settings']['link_to'] ) ) {
     78 					$element['settings']['link_to'] = 'custom';
     79 				}
     80 
     81 				return $element;
     82 			} );
     83 
     84 			$document = Plugin::$instance->documents->get( $post_id );
     85 
     86 			$document->save( [
     87 				'elements' => $data,
     88 			] );
     89 		}
     90 	}
     91 
     92 	/**
     93 	 * Upgrade Elementor 0.9.2
     94 	 *
     95 	 * Change the icon widget, icon-box widget and the social-icons widget,
     96 	 * setting their icon padding size to an empty string.
     97 	 *
     98 	 * Change the image widget, setting the image size to full image size.
     99 	 *
    100 	 * @since 2.0.0
    101 	 * @static
    102 	 * @access public
    103 	 */
    104 	public static function _v_0_9_2() {
    105 		global $wpdb;
    106 
    107 		// Fix Icon/Icon Box Widgets padding.
    108 		$post_ids = $wpdb->get_col(
    109 			'SELECT `post_id` FROM `' . $wpdb->postmeta . '`
    110 					WHERE `meta_key` = \'_elementor_version\'
    111 						AND `meta_value` = \'0.2\';'
    112 		);
    113 
    114 		if ( empty( $post_ids ) ) {
    115 			return;
    116 		}
    117 
    118 		foreach ( $post_ids as $post_id ) {
    119 			$document = Plugin::$instance->documents->get( $post_id );
    120 
    121 			if ( $document ) {
    122 				$data = $document->get_elements_data();
    123 			}
    124 
    125 			if ( empty( $data ) ) {
    126 				continue;
    127 			}
    128 
    129 			$data = Plugin::$instance->db->iterate_data( $data, function( $element ) {
    130 				if ( empty( $element['widgetType'] ) ) {
    131 					return $element;
    132 				}
    133 
    134 				if ( in_array( $element['widgetType'], [ 'icon', 'icon-box', 'social-icons' ] ) ) {
    135 					if ( ! empty( $element['settings']['icon_padding']['size'] ) ) {
    136 						$element['settings']['icon_padding']['size'] = '';
    137 					}
    138 				}
    139 
    140 				if ( 'image' === $element['widgetType'] ) {
    141 					if ( empty( $element['settings']['image_size'] ) ) {
    142 						$element['settings']['image_size'] = 'full';
    143 					}
    144 				}
    145 
    146 				return $element;
    147 			} );
    148 
    149 			$document = Plugin::$instance->documents->get( $post_id );
    150 
    151 			$document->save( [
    152 				'elements' => $data,
    153 			] );
    154 		}
    155 	}
    156 
    157 	/**
    158 	 * Upgrade Elementor 0.11.0
    159 	 *
    160 	 * Change the button widget sizes, setting up new button sizes.
    161 	 *
    162 	 * @since 2.0.0
    163 	 * @static
    164 	 * @access public
    165 	 */
    166 	public static function _v_0_11_0() {
    167 		global $wpdb;
    168 
    169 		// Fix Button widget to new sizes options.
    170 		$post_ids = $wpdb->get_col(
    171 			'SELECT `post_id` FROM `' . $wpdb->postmeta . '`
    172 					WHERE `meta_key` = \'_elementor_version\'
    173 						AND `meta_value` = \'0.3\';'
    174 		);
    175 
    176 		if ( empty( $post_ids ) ) {
    177 			return;
    178 		}
    179 
    180 		foreach ( $post_ids as $post_id ) {
    181 			$document = Plugin::$instance->documents->get( $post_id );
    182 
    183 			if ( $document ) {
    184 				$data = $document->get_elements_data();
    185 			}
    186 
    187 			if ( empty( $data ) ) {
    188 				continue;
    189 			}
    190 
    191 			$data = Plugin::$instance->db->iterate_data( $data, function( $element ) {
    192 				if ( empty( $element['widgetType'] ) ) {
    193 					return $element;
    194 				}
    195 
    196 				if ( 'button' === $element['widgetType'] ) {
    197 					$size_to_replace = [
    198 						'small' => 'xs',
    199 						'medium' => 'sm',
    200 						'large' => 'md',
    201 						'xl' => 'lg',
    202 						'xxl' => 'xl',
    203 					];
    204 
    205 					if ( ! empty( $element['settings']['size'] ) ) {
    206 						$old_size = $element['settings']['size'];
    207 
    208 						if ( isset( $size_to_replace[ $old_size ] ) ) {
    209 							$element['settings']['size'] = $size_to_replace[ $old_size ];
    210 						}
    211 					}
    212 				}
    213 
    214 				return $element;
    215 			} );
    216 
    217 			$document = Plugin::$instance->documents->get( $post_id );
    218 
    219 			$document->save( [
    220 				'elements' => $data,
    221 			] );
    222 		}
    223 	}
    224 
    225 	/**
    226 	 * Upgrade Elementor 2.0.0
    227 	 *
    228 	 * Fix post titles for old autosave drafts that saved with the format 'Auto Save 2018-03-18 17:24'.
    229 	 *
    230 	 * @static
    231 	 * @since 2.0.0
    232 	 * @access public
    233 	 */
    234 	public static function _v_2_0_0() {
    235 		global $wpdb;
    236 
    237 		$posts = $wpdb->get_results(
    238 			'SELECT `ID`, `post_title`, `post_parent`
    239 					FROM `' . $wpdb->posts . '` p
    240 					LEFT JOIN `' . $wpdb->postmeta . '` m ON p.ID = m.post_id
    241 					WHERE `post_status` = \'inherit\'
    242 					AND `post_title` = CONCAT(\'Auto Save \', DATE_FORMAT(post_date, "%Y-%m-%d %H:%i"))
    243 					AND  m.`meta_key` = \'_elementor_data\';'
    244 		);
    245 
    246 		if ( empty( $posts ) ) {
    247 			return;
    248 		}
    249 
    250 		foreach ( $posts as $post ) {
    251 			wp_update_post( [
    252 				'ID' => $post->ID,
    253 				'post_title' => get_the_title( $post->post_parent ),
    254 			] );
    255 		}
    256 	}
    257 
    258 	/**
    259 	 * Upgrade Elementor 2.0.1
    260 	 *
    261 	 * Fix post titles for old autosave drafts that saved with the format 'Auto Save...'.
    262 	 *
    263 	 * @since 2.0.2
    264 	 * @static
    265 	 * @access public
    266 	 */
    267 	public static function _v_2_0_1() {
    268 		global $wpdb;
    269 
    270 		$posts = $wpdb->get_results(
    271 			'SELECT `ID`, `post_title`, `post_parent`
    272 					FROM `' . $wpdb->posts . '` p
    273 					LEFT JOIN `' . $wpdb->postmeta . '` m ON p.ID = m.post_id
    274 					WHERE `post_status` = \'inherit\'
    275 					AND `post_title` REGEXP \'^Auto Save [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$\'
    276 					AND  m.`meta_key` = \'_elementor_data\';'
    277 		);
    278 
    279 		if ( empty( $posts ) ) {
    280 			return;
    281 		}
    282 
    283 		foreach ( $posts as $post ) {
    284 			$parent = get_post( $post->post_parent );
    285 			$title = isset( $parent->post_title ) ? $parent->post_title : '';
    286 
    287 			wp_update_post( [
    288 				'ID' => $post->ID,
    289 				'post_title' => $title,
    290 			] );
    291 		}
    292 	}
    293 
    294 	/**
    295 	 * Upgrade Elementor 2.0.10
    296 	 *
    297 	 * Fix post titles for old autosave drafts that saved with the format 'Auto Save...'.
    298 	 * Fix also Translated titles.
    299 	 *
    300 	 * @since 2.0.10
    301 	 * @static
    302 	 * @access public
    303 	 */
    304 	public static function _v_2_0_10() {
    305 		global $wpdb;
    306 
    307 		$posts = $wpdb->get_results(
    308 			'SELECT `ID`, `post_title`, `post_parent`
    309 					FROM `' . $wpdb->posts . '` p
    310 					LEFT JOIN `' . $wpdb->postmeta . '` m ON p.ID = m.post_id
    311 					WHERE `post_status` = \'inherit\'
    312 					AND `post_title` REGEXP \'[[:alnum:]]+ [[:alnum:]]+ [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$\'
    313 					AND  m.`meta_key` = \'_elementor_data\';'
    314 		);
    315 
    316 		if ( empty( $posts ) ) {
    317 			return;
    318 		}
    319 
    320 		foreach ( $posts as $post ) {
    321 			$parent = get_post( $post->post_parent );
    322 			$title = isset( $parent->post_title ) ? $parent->post_title : '';
    323 
    324 			wp_update_post( [
    325 				'ID' => $post->ID,
    326 				'post_title' => $title,
    327 			] );
    328 		}
    329 	}
    330 
    331 	public static function _v_2_1_0() {
    332 		global $wpdb;
    333 
    334 		// upgrade `video` widget settings (merge providers).
    335 		$post_ids = $wpdb->get_col(
    336 			'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND `meta_value` LIKE \'%"widgetType":"video"%\';'
    337 		);
    338 
    339 		if ( empty( $post_ids ) ) {
    340 			return;
    341 		}
    342 
    343 		foreach ( $post_ids as $post_id ) {
    344 			$do_update = false;
    345 			$document = Plugin::$instance->documents->get( $post_id );
    346 
    347 			if ( $document ) {
    348 				$data = $document->get_elements_data();
    349 			}
    350 
    351 			if ( empty( $data ) ) {
    352 				continue;
    353 			}
    354 
    355 			$data = Plugin::$instance->db->iterate_data( $data, function( $element ) use ( &$do_update ) {
    356 				if ( empty( $element['widgetType'] ) || 'video' !== $element['widgetType'] ) {
    357 					return $element;
    358 				}
    359 
    360 				$replacements = [];
    361 
    362 				if ( empty( $element['settings']['video_type'] ) || 'youtube' === $element['settings']['video_type'] ) {
    363 					$replacements = [
    364 						'yt_autoplay' => 'autoplay',
    365 						'yt_controls' => 'controls',
    366 						'yt_mute' => 'mute',
    367 						'yt_rel' => 'rel',
    368 						'link' => 'youtube_url',
    369 					];
    370 				} elseif ( 'vimeo' === $element['settings']['video_type'] ) {
    371 					$replacements = [
    372 						'vimeo_autoplay' => 'autoplay',
    373 						'vimeo_loop' => 'loop',
    374 						'vimeo_color' => 'color',
    375 						'vimeo_link' => 'vimeo_url',
    376 					];
    377 				}
    378 
    379 				// cleanup old unused settings.
    380 				unset( $element['settings']['yt_rel_videos'] );
    381 
    382 				foreach ( $replacements as $old => $new ) {
    383 					if ( ! empty( $element['settings'][ $old ] ) ) {
    384 						$element['settings'][ $new ] = $element['settings'][ $old ];
    385 						$do_update = true;
    386 					}
    387 				}
    388 
    389 				return $element;
    390 			} );
    391 
    392 			// Only update if needed.
    393 			if ( ! $do_update ) {
    394 				continue;
    395 			}
    396 
    397 			// We need the `wp_slash` in order to avoid the unslashing during the `update_post_meta`
    398 			$json_value = wp_slash( wp_json_encode( $data ) );
    399 
    400 			update_metadata( 'post', $post_id, '_elementor_data', $json_value );
    401 
    402 			// Clear WP cache for next step.
    403 			wp_cache_flush();
    404 		} // End foreach().
    405 	}
    406 
    407 	/**
    408 	 * @param Updater $updater
    409 	 *
    410 	 * @return bool
    411 	 */
    412 	public static function _v_2_3_0_widget_image( $updater ) {
    413 		global $wpdb;
    414 
    415 		// upgrade `video` widget settings (merge providers).
    416 		$post_ids = $updater->query_col(
    417 			'SELECT `post_id` FROM `' . $wpdb->postmeta . '` WHERE `meta_key` = "_elementor_data" AND (
    418 			`meta_value` LIKE \'%"widgetType":"image"%\'
    419 			OR `meta_value` LIKE \'%"widgetType":"theme-post-featured-image"%\'
    420 			OR `meta_value` LIKE \'%"widgetType":"theme-site-logo"%\'
    421 			OR `meta_value` LIKE \'%"widgetType":"woocommerce-category-image"%\'
    422 			);'
    423 		);
    424 
    425 		if ( empty( $post_ids ) ) {
    426 			return false;
    427 		}
    428 
    429 		$widgets = [
    430 			'image',
    431 			'theme-post-featured-image',
    432 			'theme-site-logo',
    433 			'woocommerce-category-image',
    434 		];
    435 
    436 		foreach ( $post_ids as $post_id ) {
    437 			// Clear WP cache for next step.
    438 			wp_cache_flush();
    439 
    440 			$do_update = false;
    441 
    442 			$document = Plugin::$instance->documents->get( $post_id );
    443 
    444 			if ( ! $document ) {
    445 				continue;
    446 			}
    447 
    448 			$data = $document->get_elements_data();
    449 
    450 			if ( empty( $data ) ) {
    451 				continue;
    452 			}
    453 
    454 			$data = Plugin::$instance->db->iterate_data( $data, function( $element ) use ( &$do_update, $widgets ) {
    455 				if ( empty( $element['widgetType'] ) || ! in_array( $element['widgetType'], $widgets ) ) {
    456 					return $element;
    457 				}
    458 
    459 				if ( ! empty( $element['settings']['caption'] ) ) {
    460 					if ( ! isset( $element['settings']['caption_source'] ) ) {
    461 						$element['settings']['caption_source'] = 'custom';
    462 
    463 						$do_update = true;
    464 					}
    465 				}
    466 
    467 				return $element;
    468 			} );
    469 
    470 			// Only update if needed.
    471 			if ( ! $do_update ) {
    472 				continue;
    473 			}
    474 
    475 			// We need the `wp_slash` in order to avoid the unslashing during the `update_post_meta`
    476 			$json_value = wp_slash( wp_json_encode( $data ) );
    477 
    478 			update_metadata( 'post', $post_id, '_elementor_data', $json_value );
    479 		} // End foreach().
    480 
    481 		return $updater->should_run_again( $post_ids );
    482 	}
    483 
    484 	/**
    485 	 * @param Updater $updater
    486 	 *
    487 	 * @return bool
    488 	 */
    489 	public static function _v_2_3_0_template_type( $updater ) {
    490 		global $wpdb;
    491 
    492 		$post_ids = $updater->query_col(
    493 			'SELECT p.ID
    494 					FROM `' . $wpdb->posts . '` AS p
    495 					LEFT JOIN `' . $wpdb->postmeta . '` AS pm1 ON (p.ID = pm1.post_id)
    496 					LEFT JOIN `' . $wpdb->postmeta . '` AS pm2 ON (pm1.post_id = pm2.post_id AND pm2.meta_key = "_elementor_template_type")
    497 					WHERE p.post_status != "inherit" AND pm1.`meta_key` = "_elementor_data" AND pm2.post_id IS NULL;'
    498 		);
    499 
    500 		if ( empty( $post_ids ) ) {
    501 			return false;
    502 		}
    503 
    504 		foreach ( $post_ids as $post_id ) {
    505 			// Clear WP cache for next step.
    506 			wp_cache_flush();
    507 
    508 			$document = Plugin::$instance->documents->get( $post_id );
    509 
    510 			if ( ! $document ) {
    511 				continue;
    512 			}
    513 
    514 			$document->save_template_type();
    515 		} // End foreach().
    516 
    517 		return $updater->should_run_again( $post_ids );
    518 	}
    519 
    520 	/**
    521 	 * Set FontAwesome Migration needed flag
    522 	 */
    523 	public static function _v_2_6_0_fa4_migration_flag() {
    524 		add_option( 'elementor_icon_manager_needs_update', 'yes' );
    525 		add_option( 'elementor_load_fa4_shim', 'yes' );
    526 	}
    527 
    528 	/**
    529 	 * migrate Icon control string value to Icons control array value
    530 	 *
    531 	 * @param array $element
    532 	 * @param array $args
    533 	 *
    534 	 * @return mixed
    535 	 */
    536 	public static function _migrate_icon_fa4_value( $element, $args ) {
    537 		$widget_id = $args['widget_id'];
    538 
    539 		if ( empty( $element['widgetType'] ) || $widget_id !== $element['widgetType'] ) {
    540 			return $element;
    541 		}
    542 		foreach ( $args['control_ids'] as $old_name => $new_name ) {
    543 			// exit if new value exists
    544 			if ( isset( $element['settings'][ $new_name ] ) ) {
    545 				continue;
    546 			}
    547 
    548 			// exit if no value to migrate
    549 			if ( ! isset( $element['settings'][ $old_name ] ) ) {
    550 				continue;
    551 			}
    552 
    553 			$element['settings'][ $new_name ] = Icons_Manager::fa4_to_fa5_value_migration( $element['settings'][ $old_name ] );
    554 			$args['do_update'] = true;
    555 		}
    556 		return $element;
    557 	}
    558 
    559 	/**
    560 	 * Set FontAwesome 5 value Migration on for button widget
    561 	 *
    562 	 * @param Updater $updater
    563 	 */
    564 	public static function _v_2_6_6_fa4_migration_button( $updater ) {
    565 		$changes = [
    566 			[
    567 				'callback' => [ 'Elementor\Core\Upgrade\Upgrades', '_migrate_icon_fa4_value' ],
    568 				'control_ids' => [
    569 					'icon' => 'selected_icon',
    570 				],
    571 			],
    572 		];
    573 		Upgrade_Utils::_update_widget_settings( 'button', $updater, $changes );
    574 		Upgrade_Utils::_update_widget_settings( 'icon-box', $updater, $changes );
    575 	}
    576 
    577 	/**
    578 	 *  Update database to separate page from post.
    579 	 *
    580 	 * @param Updater $updater
    581 	 *
    582 	 * @param string $type
    583 	 *
    584 	 * @return bool
    585 	 */
    586 	public static function rename_document_base_to_wp( $updater, $type ) {
    587 		global $wpdb;
    588 
    589 		$post_ids = $updater->query_col( $wpdb->prepare(
    590 			"SELECT p1.ID FROM {$wpdb->posts} AS p
    591 					LEFT JOIN {$wpdb->posts} AS p1 ON (p.ID = p1.post_parent || p.ID = p1.ID)
    592 					WHERE p.post_type = %s;", $type ) );
    593 
    594 		if ( empty( $post_ids ) ) {
    595 			return false;
    596 		}
    597 
    598 		$sql_post_ids = implode( ',', $post_ids );
    599 
    600 		$wpdb->query( $wpdb->prepare(
    601 			"UPDATE $wpdb->postmeta SET meta_value = %s
    602 			WHERE meta_key = '_elementor_template_type' && post_id in ( %s );
    603 		 ", 'wp-' . $type, $sql_post_ids ) );
    604 
    605 		return $updater->should_run_again( $post_ids );
    606 	}
    607 
    608 	/**
    609 	 *  Update database to separate page from post.
    610 	 *
    611 	 * @param Updater $updater
    612 	 *
    613 	 * @return bool
    614 	 */
    615 	// Because the query is slow on large sites, temporary don't upgrade.
    616 	/*	public static function _v_2_7_0_rename_document_types_to_wp( $updater ) {
    617 		return self::rename_document_base_to_wp( $updater, 'post' ) || self::rename_document_base_to_wp( $updater, 'page' );
    618 	}*/
    619 
    620 	// Upgrade code was fixed & moved to _v_2_7_1_remove_old_usage_data.
    621 	/* public static function _v_2_7_0_remove_old_usage_data() {} */
    622 
    623 	// Upgrade code moved to _v_2_7_1_recalc_usage_data.
    624 	/* public static function _v_2_7_0_recalc_usage_data( $updater ) {} */
    625 
    626 	/**
    627 	 * Don't use the old data anymore.
    628 	 * Since 2.7.1 the key was changed from `elementor_elements_usage` to `elementor_controls_usage`.
    629 	 */
    630 	public static function _v_2_7_1_remove_old_usage_data() {
    631 		delete_option( 'elementor_elements_usage' );
    632 		delete_post_meta_by_key( '_elementor_elements_usage' );
    633 	}
    634 
    635 	/**
    636 	 * Recalc usage.
    637 	 *
    638 	 * @param Updater $updater
    639 	 *
    640 	 * @return bool
    641 	 */
    642 	public static function recalc_usage_data( $updater ) {
    643 		/** @var Module $module */
    644 		$module = Plugin::$instance->modules_manager->get_modules( 'usage' );
    645 
    646 		$post_count = $module->recalc_usage( $updater->get_limit(), $updater->get_current_offset() );
    647 
    648 		return ( $post_count === $updater->get_limit() );
    649 	}
    650 
    651 	public static function _v_2_7_1_recalc_usage_data( $updater ) {
    652 		return self::recalc_usage_data( $updater );
    653 	}
    654 
    655 	public static function _v_2_8_3_recalc_usage_data( $updater ) {
    656 		// Re-calc since older version(s) had invalid values.
    657 		return self::recalc_usage_data( $updater );
    658 	}
    659 
    660 	/**
    661 	 * Move general & lightbox settings to active kit and all it's revisions.
    662 	 *
    663 	 * @param Updater $updater
    664 	 *
    665 	 * @return bool
    666 	 */
    667 	public static function _v_3_0_0_move_general_settings_to_kit( $updater ) {
    668 		$callback = function( $kit_id ) {
    669 			$kit = Plugin::$instance->documents->get( $kit_id );
    670 
    671 			if ( ! $kit ) {
    672 				self::notice( 'Kit not found. nothing to do.' );
    673 				return;
    674 			}
    675 
    676 			$meta_key = SettingsPageManager::META_KEY;
    677 			$current_settings = get_option( '_elementor_general_settings', [] );
    678 			// Take the `space_between_widgets` from the option due to a bug on E < 3.0.0 that the value `0` is stored separated.
    679 			$current_settings['space_between_widgets'] = get_option( 'elementor_space_between_widgets', '' );
    680 			$current_settings[ Breakpoints_Manager::BREAKPOINT_SETTING_PREFIX . 'md' ] = get_option( 'elementor_viewport_md', '' );
    681 			$current_settings[ Breakpoints_Manager::BREAKPOINT_SETTING_PREFIX . 'lg' ] = get_option( 'elementor_viewport_lg', '' );
    682 
    683 			$kit_settings = $kit->get_meta( $meta_key );
    684 
    685 			// Already exist.
    686 			if ( isset( $kit_settings['default_generic_fonts'] ) ) {
    687 				self::notice( 'General Settings already exist. nothing to do.' );
    688 				return;
    689 			}
    690 
    691 			if ( empty( $current_settings ) ) {
    692 				self::notice( 'Current settings are empty. nothing to do.' );
    693 				return;
    694 			}
    695 
    696 			if ( ! $kit_settings ) {
    697 				$kit_settings = [];
    698 			}
    699 
    700 			// Convert some setting to Elementor slider format.
    701 			$settings_to_slider = [
    702 				'container_width',
    703 				'space_between_widgets',
    704 			];
    705 
    706 			foreach ( $settings_to_slider as $setting ) {
    707 				if ( isset( $current_settings[ $setting ] ) ) {
    708 					$current_settings[ $setting ] = [
    709 						'unit' => 'px',
    710 						'size' => $current_settings[ $setting ],
    711 					];
    712 				}
    713 			}
    714 
    715 			$kit_settings = array_merge( $kit_settings, $current_settings );
    716 
    717 			$page_settings_manager = SettingsManager::get_settings_managers( 'page' );
    718 			$page_settings_manager->save_settings( $kit_settings, $kit_id );
    719 		};
    720 
    721 		return self::move_settings_to_kit( $callback, $updater );
    722 	}
    723 
    724 	/**
    725 	 * Move default colors settings to active kit and all it's revisions.
    726 	 *
    727 	 * @param Updater $updater
    728 	 *
    729 	 * @return bool
    730 	 */
    731 	public static function _v_3_0_0_move_default_colors_to_kit( $updater, $include_revisions = true ) {
    732 		$callback = function( $kit_id ) {
    733 			if ( ! Plugin::$instance->kits_manager->is_custom_colors_enabled() ) {
    734 				self::notice( 'System colors are disabled. nothing to do.' );
    735 				return;
    736 			}
    737 
    738 			$kit = Plugin::$instance->documents->get( $kit_id );
    739 
    740 			// Already exist. use raw settings that doesn't have default values.
    741 			$meta_key = SettingsPageManager::META_KEY;
    742 			$kit_raw_settings = $kit->get_meta( $meta_key );
    743 			if ( isset( $kit_raw_settings['system_colors'] ) ) {
    744 				self::notice( 'System colors already exist. nothing to do.' );
    745 				return;
    746 			}
    747 
    748 			$scheme_obj = Plugin::$instance->schemes_manager->get_scheme( 'color' );
    749 
    750 			$default_colors = $scheme_obj->get_scheme();
    751 
    752 			$new_ids = [
    753 				'primary',
    754 				'secondary',
    755 				'text',
    756 				'accent',
    757 			];
    758 
    759 			foreach ( $default_colors as $index => $color ) {
    760 				$kit->add_repeater_row( 'system_colors', [
    761 					'_id' => $new_ids[ $index - 1 ], // $default_colors starts from 1.
    762 					'title' => $color['title'],
    763 					'color' => strtoupper( $color['value'] ),
    764 				] );
    765 			}
    766 		};
    767 
    768 		return self::move_settings_to_kit( $callback, $updater, $include_revisions );
    769 	}
    770 
    771 	/**
    772 	 * Move saved colors settings to active kit and all it's revisions.
    773 	 *
    774 	 * @param Updater $updater
    775 	 *
    776 	 * @return bool
    777 	 */
    778 	public static function _v_3_0_0_move_saved_colors_to_kit( $updater, $include_revisions = true ) {
    779 		$callback = function( $kit_id ) {
    780 			$kit = Plugin::$instance->documents->get( $kit_id );
    781 
    782 			// Already exist. use raw settings that doesn't have default values.
    783 			$meta_key = SettingsPageManager::META_KEY;
    784 			$kit_raw_settings = $kit->get_meta( $meta_key );
    785 			if ( isset( $kit_raw_settings['custom_colors'] ) ) {
    786 				self::notice( 'Custom colors already exist. nothing to do.' );
    787 				return;
    788 			}
    789 
    790 			$system_colors_rows = $kit->get_settings( 'system_colors' );
    791 
    792 			if ( ! $system_colors_rows ) {
    793 				$system_colors_rows = [];
    794 			}
    795 
    796 			$system_colors = [];
    797 
    798 			foreach ( $system_colors_rows as $color_row ) {
    799 				$system_colors[] = strtoupper( $color_row['color'] );
    800 			}
    801 
    802 			$saved_scheme_obj = Plugin::$instance->schemes_manager->get_scheme( 'color-picker' );
    803 
    804 			$current_saved_colors_rows = $saved_scheme_obj->get_scheme();
    805 
    806 			$current_saved_colors = [];
    807 
    808 			foreach ( $current_saved_colors_rows as $color_row ) {
    809 				$current_saved_colors[] = strtoupper( $color_row['value'] );
    810 			}
    811 
    812 			$colors_to_save = array_diff( $current_saved_colors, $system_colors );
    813 
    814 			if ( empty( $colors_to_save ) ) {
    815 				self::notice( 'Saved colors not found. nothing to do.' );
    816 				return;
    817 			}
    818 
    819 			foreach ( $colors_to_save as $index => $color ) {
    820 				$kit->add_repeater_row( 'custom_colors', [
    821 					'_id' => Utils::generate_random_string(),
    822 					'title' => esc_html__( 'Saved Color', 'elementor' ) . ' #' . ( $index + 1 ),
    823 					'color' => $color,
    824 				] );
    825 			}
    826 		};
    827 
    828 		return self::move_settings_to_kit( $callback, $updater, $include_revisions );
    829 	}
    830 
    831 	/**
    832 	 * Move default typography settings to active kit and all it's revisions.
    833 	 *
    834 	 * @param Updater $updater
    835 	 *
    836 	 * @return bool
    837 	 */
    838 	public static function _v_3_0_0_move_default_typography_to_kit( $updater, $include_revisions = true ) {
    839 		$callback = function( $kit_id ) {
    840 			if ( ! Plugin::$instance->kits_manager->is_custom_typography_enabled() ) {
    841 				self::notice( 'System typography is disabled. nothing to do.' );
    842 				return;
    843 			}
    844 
    845 			$kit = Plugin::$instance->documents->get( $kit_id );
    846 
    847 			// Already exist. use raw settings that doesn't have default values.
    848 			$meta_key = SettingsPageManager::META_KEY;
    849 			$kit_raw_settings = $kit->get_meta( $meta_key );
    850 			if ( isset( $kit_raw_settings['system_typography'] ) ) {
    851 				self::notice( 'System typography already exist. nothing to do.' );
    852 				return;
    853 			}
    854 
    855 			$scheme_obj = Plugin::$instance->schemes_manager->get_scheme( 'typography' );
    856 
    857 			$default_typography = $scheme_obj->get_scheme();
    858 
    859 			$new_ids = [
    860 				'primary',
    861 				'secondary',
    862 				'text',
    863 				'accent',
    864 			];
    865 
    866 			foreach ( $default_typography as $index => $typography ) {
    867 				$kit->add_repeater_row( 'system_typography', [
    868 					'_id' => $new_ids[ $index - 1 ], // $default_typography starts from 1.
    869 					'title' => $typography['title'],
    870 					'typography_typography' => 'custom',
    871 					'typography_font_family' => $typography['value']['font_family'],
    872 					'typography_font_weight' => $typography['value']['font_weight'],
    873 				] );
    874 			}
    875 		};
    876 
    877 		return self::move_settings_to_kit( $callback, $updater, $include_revisions );
    878 	}
    879 
    880 	public static function v_3_1_0_move_optimized_dom_output_to_experiments() {
    881 		$saved_option = get_option( 'elementor_optimized_dom_output' );
    882 
    883 		if ( $saved_option ) {
    884 			$new_option = 'enabled' === $saved_option ? Experiments_Manager::STATE_ACTIVE : Experiments_Manager::STATE_INACTIVE;
    885 
    886 			add_option( 'elementor_experiment-e_dom_optimization', $new_option );
    887 		}
    888 	}
    889 
    890 	public static function _v_3_2_0_migrate_breakpoints_to_new_system( $updater, $include_revisions = true ) {
    891 		$callback = function( $kit_id ) {
    892 			$kit = Plugin::$instance->documents->get( $kit_id );
    893 
    894 			$kit_settings = $kit->get_meta( SettingsPageManager::META_KEY );
    895 
    896 			if ( ! $kit_settings ) {
    897 				// Nothing to upgrade.
    898 				return;
    899 			}
    900 
    901 			$prefix = Breakpoints_Manager::BREAKPOINT_SETTING_PREFIX;
    902 			$old_mobile_option_key = $prefix . 'md';
    903 			$old_tablet_option_key = $prefix . 'lg';
    904 
    905 			$breakpoint_values = [
    906 				$old_mobile_option_key => Plugin::$instance->kits_manager->get_current_settings( $old_mobile_option_key ),
    907 				$old_tablet_option_key => Plugin::$instance->kits_manager->get_current_settings( $old_tablet_option_key ),
    908 			];
    909 
    910 			// Breakpoint values are either a number, or an empty string (empty setting).
    911 			array_walk( $breakpoint_values, function( &$breakpoint_value, $breakpoint_key ) {
    912 				if ( $breakpoint_value ) {
    913 					// If the saved breakpoint value is a number, 1px is reduced because the new breakpoints system is
    914 					// based on max-width, as opposed to the old breakpoints system that worked based on min-width.
    915 					$breakpoint_value--;
    916 				}
    917 
    918 				return $breakpoint_value;
    919 			} );
    920 
    921 			$kit_settings[ $prefix . Breakpoints_Manager::BREAKPOINT_KEY_MOBILE ] = $breakpoint_values[ $old_mobile_option_key ];
    922 			$kit_settings[ $prefix . Breakpoints_Manager::BREAKPOINT_KEY_TABLET ] = $breakpoint_values[ $old_tablet_option_key ];
    923 
    924 			$page_settings_manager = SettingsManager::get_settings_managers( 'page' );
    925 			$page_settings_manager->save_settings( $kit_settings, $kit_id );
    926 		};
    927 
    928 		return self::move_settings_to_kit( $callback, $updater, $include_revisions );
    929 	}
    930 
    931 	/**
    932 	 * @param callback $callback
    933 	 * @param Updater  $updater
    934 	 *
    935 	 * @param bool     $include_revisions
    936 	 *
    937 	 * @return mixed
    938 	 */
    939 	private static function move_settings_to_kit( $callback, $updater, $include_revisions = true ) {
    940 		$active_kit_id = Plugin::$instance->kits_manager->get_active_id();
    941 		if ( ! $active_kit_id ) {
    942 			self::notice( 'Active kit not found. nothing to do.' );
    943 			return false;
    944 		}
    945 
    946 		$offset = $updater->get_current_offset();
    947 
    948 		// On first iteration apply on active kit itself.
    949 		// (don't include it with revisions in order to avoid offset/iteration count wrong numbers)
    950 		if ( 0 === $offset ) {
    951 			$callback( $active_kit_id );
    952 		}
    953 
    954 		if ( ! $include_revisions ) {
    955 			return false;
    956 		}
    957 
    958 		$revisions_ids = wp_get_post_revisions( $active_kit_id, [
    959 			'fields' => 'ids',
    960 			'posts_per_page' => $updater->get_limit(),
    961 			'offset' => $offset,
    962 		] );
    963 
    964 		foreach ( $revisions_ids as $revision_id ) {
    965 			$callback( $revision_id );
    966 		}
    967 
    968 		return $updater->should_run_again( $revisions_ids );
    969 	}
    970 
    971 	private static function notice( $message ) {
    972 		$logger = Plugin::$instance->logger->get_logger();
    973 		$logger->notice( $message );
    974 	}
    975 }