PluginProbe
Elementor Website Builder – more than just a page builder / 4.0.0-dev4
Elementor Website Builder – more than just a page builder v4.0.0-dev4
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / core / upgrade / upgrades.php

upgrades.php in Elementor Website Builder – more than just a page builder 4.0.0-dev4, at core/upgrade/upgrades.php

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