PluginProbe
Elementor Website Builder – more than just a page builder / 3.27.4
Elementor Website Builder – more than just a page builder v3.27.4
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
← All changes | core/experiments/manager.php +72 -67 4.2.0-dev23.27.4 View file →
@@ -11,9 +11,9 @@
11 11 use Elementor\Tracker;
12 12 use Elementor\Utils;
13 13
14 14 if ( ! defined( 'ABSPATH' ) ) {
15 - exit; // Exit if accessed directly.
15 + exit; // Exit if accessed directly
16 16 }
17 17
18 18 class Manager extends Base_Object {
19 19
@@ -22,8 +22,10 @@
22 22 const RELEASE_STATUS_ALPHA = 'alpha';
23 23
24 24 const RELEASE_STATUS_BETA = 'beta';
25 25
26 + const RELEASE_STATUS_RC = 'rc';
27 +
26 28 const RELEASE_STATUS_STABLE = 'stable';
27 29
28 30 const STATE_DEFAULT = 'default';
29 31
@@ -43,26 +45,24 @@
43 45
44 46 /**
45 47 * Add Feature
46 48 *
47 - * Each feature has to provide the following information:
48 - * [
49 - * 'name' => string,
50 - * 'title' => string,
51 - * 'description' => string,
52 - * 'tag' => string,
53 - * 'release_status' => string,
54 - * 'default' => string,
55 - * 'new_site' => array,
56 - * ]
57 - *
58 49 * @since 3.1.0
59 50 * @access public
60 51 *
61 - * @param array $options Feature options.
52 + * @param array $options {
53 + * @type string $name
54 + * @type string $title
55 + * @type string $tag
56 + * @type array $tags
57 + * @type string $description
58 + * @type string $release_status
59 + * @type string $default
60 + * @type callable $on_state_change
61 + * }
62 + *
62 63 * @return array|null
63 - *
64 - * @throws Dependency_Exception If can't change feature state.
64 + * @throws \Exception
65 65 */
66 66 public function add_feature( array $options ) {
67 67 if ( isset( $this->features[ $options['name'] ] ) ) {
68 68 return null;
@@ -120,11 +120,9 @@
120 120 private function install_compare( $version ) {
121 121 $installs_history = Upgrade_Manager::get_installs_history();
122 122
123 123 if ( empty( $installs_history ) ) {
124 - // Fresh installation: upgrade manager hasn't written history yet on this first request.
125 - // Use the current plugin version as the effective first-install version.
126 - return version_compare( ELEMENTOR_VERSION, $version, '>=' );
124 + return false;
127 125 }
128 126
129 127 $cleaned_version = preg_replace( '/-(beta|cloud|dev)\d*$/', '', key( $installs_history ) );
130 128
@@ -141,9 +139,9 @@
141 139 * @param array $experimental_data
142 140 *
143 141 * @return array
144 142 */
145 - private function unify_feature_tags( array $experimental_data ): array {
143 + private function unify_feature_tags( array $experimental_data ) : array {
146 144 foreach ( [ 'tag', 'tags' ] as $key ) {
147 145 if ( empty( $experimental_data[ $key ] ) ) {
148 146 continue;
149 147 }
@@ -160,21 +158,18 @@
160 158
161 159 /**
162 160 * Format feature tags into the right format.
163 161 *
164 - * If an array of tags provided, each tag has to provide the following information:
165 - * [
166 - * [
167 - * 'type' => string,
168 - * 'label' => string,
169 - * ]
170 - * ]
162 + * @param string|array[
163 + * [
164 + * 'type' => string,
165 + * 'label' => string
166 + * ]
167 + * ] $tag
171 168 *
172 - * @param string|array $tags A string of comma separated tags, or an array of tags.
173 - *
174 169 * @return array
175 170 */
176 - private function format_feature_tags( $tags ): array {
171 + private function format_feature_tags( $tags ) : array {
177 172 if ( ! is_string( $tags ) && ! is_array( $tags ) ) {
178 173 return [];
179 174 }
180 175
@@ -223,9 +218,9 @@
223 218 *
224 219 * @since 3.1.0
225 220 * @access public
226 221 *
227 - * @param string $feature_name Optional. Default is null.
222 + * @param string $feature_name Optional. Default is null
228 223 *
229 224 * @return array|null
230 225 */
231 226 public function get_features( $feature_name = null ) {
@@ -342,9 +337,8 @@
342 337 $this->add_feature( [
343 338 'name' => 'container',
344 339 'title' => esc_html__( 'Container', 'elementor' ),
345 340 'description' => sprintf(
346 - /* translators: 1: Link opening tag, 2: Link closing tag, 3: Link opening tag, 4: Link closing tag, 5: Link opening tag, 6: Link closing tag */
347 341 esc_html__( 'Create advanced layouts and responsive designs with %1$sFlexbox%2$s and %3$sGrid%4$s container elements. Give it a try using the %5$sContainer playground%6$s.', 'elementor' ),
348 342 '<a target="_blank" href="https://go.elementor.com/wp-dash-flex-container/">',
349 343 '</a>',
350 344 '<a target="_blank" href="https://go.elementor.com/wp-dash-grid-container/">',
@@ -366,29 +360,45 @@
366 360 ),
367 361 ],
368 362 ] );
369 363
364 + // TODO: Remove this experiment in v3.28 [ED-15983].
370 365 $this->add_feature( [
366 + 'name' => 'e_swiper_latest',
367 + 'title' => esc_html__( 'Upgrade Swiper Library', 'elementor' ),
368 + 'description' => esc_html__( 'Prepare your website for future improvements to carousel features by upgrading the Swiper library integrated into your site from v5.36 to v8.45. This experiment includes markup changes so it might require updating custom code and cause compatibility issues with third party plugins.', 'elementor' ),
369 + 'default' => self::STATE_ACTIVE,
370 + static::TYPE_HIDDEN => true,
371 + 'mutable' => false,
372 + ] );
373 +
374 + $this->add_feature( [
371 375 'name' => 'e_optimized_markup',
372 376 'title' => esc_html__( 'Optimized Markup', 'elementor' ),
373 377 'tag' => esc_html__( 'Performance', 'elementor' ),
374 378 'description' => esc_html__( 'Reduce the DOM size by eliminating HTML tags in various elements and widgets. This experiment includes markup changes so it might require updating custom CSS/JS code and cause compatibility issues with third party plugins.', 'elementor' ),
375 - 'release_status' => self::RELEASE_STATUS_STABLE,
379 + 'release_status' => self::RELEASE_STATUS_ALPHA,
376 380 'default' => self::STATE_INACTIVE,
377 - 'new_site' => [
378 - 'default_active' => true,
379 - 'minimum_installation_version' => '3.30.0',
380 - ],
381 381 ] );
382 382
383 383 $this->add_feature( [
384 - 'name' => 'e_panel_promotions',
385 - 'title' => esc_html__( 'Panel Promotions', 'elementor' ),
386 - 'description' => esc_html__( 'Enable experimental rendering for targeted promotions within the elements panels.', 'elementor' ),
387 - 'release_status' => self::RELEASE_STATUS_DEV,
384 + 'name' => 'e_onboarding',
385 + 'title' => esc_html__( 'Plugin Onboarding', 'elementor' ),
386 + 'description' => esc_html__( 'New plugin onboarding.', 'elementor' ),
387 + static::TYPE_HIDDEN => true,
388 + 'release_status' => self::RELEASE_STATUS_ALPHA,
388 389 'default' => self::STATE_ACTIVE,
389 - 'type' => self::TYPE_HIDDEN,
390 390 ] );
391 +
392 + $this->add_feature( [
393 + 'name' => 'e_local_google_fonts',
394 + 'title' => esc_html__( 'Load Google Fonts locally', 'elementor' ),
395 + 'description' => esc_html__( "To improve page load performance and user privacy, replace Google Fonts CDN links with self-hosted font files. This approach downloads and serves font files directly from your server, eliminating external requests to Google's servers.", 'elementor' ),
396 + 'tag' => esc_html__( 'Performance', 'elementor' ),
397 + 'release_status' => self::RELEASE_STATUS_BETA,
398 + 'generator_tag' => true,
399 + 'default' => self::STATE_INACTIVE,
400 + ] );
391 401 }
392 402
393 403 /**
394 404 * Init States
@@ -414,8 +424,9 @@
414 424 $this->release_statuses = [
415 425 self::RELEASE_STATUS_DEV => esc_html__( 'Development', 'elementor' ),
416 426 self::RELEASE_STATUS_ALPHA => esc_html__( 'Alpha', 'elementor' ),
417 427 self::RELEASE_STATUS_BETA => esc_html__( 'Beta', 'elementor' ),
428 + self::RELEASE_STATUS_RC => esc_html__( 'Release Candidate', 'elementor' ),
418 429 self::RELEASE_STATUS_STABLE => esc_html__( 'Stable', 'elementor' ),
419 430 ];
420 431 }
421 432
@@ -439,8 +450,9 @@
439 450 * @param Settings $settings
440 451 *
441 452 * @since 3.1.0
442 453 * @access private
454 + *
443 455 */
444 456 private function register_settings_fields( Settings $settings ) {
445 457 $features = $this->get_features();
446 458
@@ -528,9 +540,9 @@
528 540 <?php echo esc_html__( 'Experiments and Features', 'elementor' ); ?>
529 541 </h2>
530 542 <p class="e-experiment__description">
531 543 <?php
532 - printf(
544 + echo sprintf(
533 545 /* translators: %1$s Link open tag, %2$s: Link close tag. */
534 546 esc_html__( 'Personalize your Elementor experience by controlling which features and experiments are active on your site. Help make Elementor better by %1$ssharing your experience and feedback with us%2$s.', 'elementor' ),
535 547 '<a href="https://go.elementor.com/wp-dash-experiments-report-an-issue/" target="_blank">',
536 548 '</a>'
@@ -538,9 +550,9 @@
538 550 ?>
539 551 </p>
540 552 <p class="e-experiment__description">
541 553 <?php
542 - printf(
554 + echo sprintf(
543 555 '%1$s <a href="https://go.elementor.com/wp-dash-experiments/" target="_blank">%2$s</a>',
544 556 esc_html__( 'To use an experiment or feature on your site, simply click on the dropdown next to it and switch to Active. You can always deactivate them at any time.', 'elementor' ),
545 557 esc_html__( 'Learn more', 'elementor' ),
546 558 );
@@ -632,13 +644,13 @@
632 644 ->find( function ( $dependency ) {
633 645 return $dependency instanceof Non_Existing_Dependency;
634 646 } );
635 647
636 - return (bool) $non_existing_dep;
648 + return ! ! $non_existing_dep;
637 649 }
638 650
639 651 /**
640 - * Get Feature Settings Label HTML.
652 + * Get Feature Settings Label HTML
641 653 *
642 654 * @since 3.1.0
643 655 * @access private
644 656 *
@@ -663,12 +675,9 @@
663 675 <div class="e-experiment__title">
664 676 <div class="<?php echo $indicator_classes; ?>" data-tooltip="<?php echo $indicator_tooltip; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"></div>
665 677 <label class="e-experiment__title__label" for="e-experiment-<?php echo $feature['name']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"><?php echo $feature['title']; ?></label>
666 678 <?php foreach ( $feature['tags'] as $tag ) { ?>
667 - <?php
668 - $tag_classes = 'e-experiment__title__tag e-experiment__title__tag__' . $tag['type'] . ' e-editor-one';
669 - ?>
670 - <span class="<?php echo esc_attr( $tag_classes ); ?>"><?php echo $tag['label']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
679 + <span class="e-experiment__title__tag e-experiment__title__tag__<?php echo $tag['type']; ?>"><?php echo $tag['label']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
671 680 <?php } ?>
672 681 <?php if ( $feature['deprecated'] ) { ?>
673 682 <span class="e-experiment__title__tag e-experiment__title__tag__deprecated"><?php echo esc_html__( 'Deprecated', 'elementor' ); ?></span>
674 683 <?php } ?>
@@ -736,11 +745,12 @@
736 745 *
737 746 * @since 3.1.0
738 747 * @access private
739 748 *
740 - * @param array $old_feature_data
749 + * @param array $old_feature_data
741 750 * @param string $new_state
742 - * @param string $old_state
751 + *
752 + * @throws \Elementor\Core\Experiments\Exceptions\Dependency_Exception
743 753 */
744 754 private function on_feature_state_change( array $old_feature_data, $new_state, $old_state ) {
745 755 $new_feature_data = $this->get_features( $old_feature_data['name'] );
746 756 $this->validate_dependency( $new_feature_data, $new_state );
@@ -757,9 +767,9 @@
757 767 do_action( 'elementor/experiments/feature-state-change/' . $old_feature_data['name'], $old_state, $new_state );
758 768 }
759 769
760 770 /**
761 - * @throws Exceptions\Dependency_Exception If the feature dependency is not available or not active.
771 + * @throws \Elementor\Core\Experiments\Exceptions\Dependency_Exception
762 772 */
763 773 private function validate_dependency( array $feature, $new_state ) {
764 774 $rollback = function ( $feature_option_key, $state ) {
765 775 remove_all_actions( 'add_option_' . $feature_option_key );
@@ -788,10 +798,10 @@
788 798
789 799 throw new Exceptions\Dependency_Exception(
790 800 sprintf(
791 801 'The feature `%s` has a dependency `%s` that is not available.',
792 - esc_html( $feature['name'] ),
793 - esc_html( $dependency->get_name() )
802 + $feature['name'],
803 + $dependency->get_name()
794 804 )
795 805 );
796 806 }
797 807
@@ -803,10 +813,10 @@
803 813
804 814 throw new Exceptions\Dependency_Exception(
805 815 sprintf(
806 816 'To turn on `%1$s`, Experiment: `%2$s` activity is required!',
807 - esc_html( $feature['name'] ),
808 - esc_html( $dependency_feature['name'] )
817 + $feature['name'],
818 + $dependency_feature['name']
809 819 )
810 820 );
811 821 }
812 822 }
@@ -851,9 +861,11 @@
851 861 * This behavior interferes with the experiments dependency mechanism because the data that's being sent can be in any order,
852 862 * while the dependencies mechanism expects it to be in a specific order (dependencies should be activated before their dependents can).
853 863 * In order to solve this issue, we sort the experiments in the POST data based on their dependencies tree.
854 864 *
855 - * @param array $allowed_options
865 + * @param $allowed_options
866 + *
867 + * @return mixed
856 868 */
857 869 private function sort_allowed_options_by_dependencies( $allowed_options ) {
858 870 if ( ! isset( $allowed_options['elementor'] ) ) {
859 871 return $allowed_options;
@@ -946,10 +958,9 @@
946 958
947 959 /**
948 960 * @param array $experimental_data
949 961 * @return array
950 - *
951 - * @throws Exceptions\Dependency_Exception If the feature dependency is not initialized or depends on a hidden experiment.
962 + * @throws Dependency_Exception
952 963 */
953 964 private function initialize_feature_dependencies( array $experimental_data ): array {
954 965 foreach ( $experimental_data['dependencies'] as $key => $dependency ) {
955 966 $feature = $this->get_features( $dependency );
@@ -955,15 +966,9 @@
955 966 $feature = $this->get_features( $dependency );
956 967
957 968 if ( ! isset( $feature ) ) {
958 969 // since we must validate the state of each dependency, we have to make sure that dependencies are initialized in the correct order, otherwise, error.
959 - throw new Exceptions\Dependency_Exception(
960 - sprintf(
961 - 'Feature %s cannot be initialized before dependency feature: %s.',
962 - esc_html( $experimental_data['name'] ),
963 - esc_html( $dependency )
964 - )
965 - );
970 + throw new Exceptions\Dependency_Exception( "Feature {$experimental_data['name']} cannot be initialized before dependency feature: {$dependency}." );
966 971 }
967 972
968 973 if ( ! empty( $feature[ static::TYPE_HIDDEN ] ) ) {
969 974 throw new Exceptions\Dependency_Exception( 'Depending on a hidden experiment is not allowed.' );
@@ -1000,9 +1005,9 @@
1000 1005 return $experimental_data;
1001 1006 }
1002 1007
1003 1008 /**
1004 - * @param array $new_site
1009 + * @param $new_site
1005 1010 * @param array $experimental_data
1006 1011 * @return array
1007 1012 */
1008 1013 private function set_new_site_default_state( $new_site, array $experimental_data ): array {