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 +74 -68 4.3.0-beta23.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,13 +337,14 @@
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 */
347 - esc_html__( 'Create advanced layouts and responsive designs with %1$sFlexbox%2$s and %3$sGrid%4$s container elements.', 'elementor' ),
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/">',
345 + '</a>',
346 + '<a target="_blank" href="https://go.elementor.com/wp-dash-flex-container-playground/">',
351 347 '</a>'
352 348 ),
353 349 'release_status' => self::RELEASE_STATUS_STABLE,
354 350 'default' => self::STATE_INACTIVE,
@@ -364,29 +360,44 @@
364 360 ),
365 361 ],
366 362 ] );
367 363
364 + // TODO: Remove this experiment in v3.28 [ED-15983].
368 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( [
369 375 'name' => 'e_optimized_markup',
370 376 'title' => esc_html__( 'Optimized Markup', 'elementor' ),
371 377 'tag' => esc_html__( 'Performance', 'elementor' ),
372 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' ),
373 - 'release_status' => self::RELEASE_STATUS_STABLE,
379 + 'release_status' => self::RELEASE_STATUS_ALPHA,
374 380 'default' => self::STATE_INACTIVE,
375 - 'new_site' => [
376 - 'default_active' => true,
377 - 'minimum_installation_version' => '3.30.0',
378 - ],
379 381 ] );
380 382
381 383 $this->add_feature( [
382 - 'name' => 'e_optimized_css_files',
383 - 'title' => esc_html__( 'Optimized CSS Files', 'elementor' ),
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,
389 + 'default' => self::STATE_ACTIVE,
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' ),
384 396 'tag' => esc_html__( 'Performance', 'elementor' ),
385 - 'description' => esc_html__( 'Keeps external CSS files available and consistent for sites behind page caching or a CDN.', 'elementor' ),
386 - 'release_status' => self::RELEASE_STATUS_ALPHA,
397 + 'release_status' => self::RELEASE_STATUS_BETA,
398 + 'generator_tag' => true,
387 399 'default' => self::STATE_INACTIVE,
388 - 'generator_tag' => true,
389 400 ] );
390 401 }
391 402
392 403 /**
@@ -413,8 +424,9 @@
413 424 $this->release_statuses = [
414 425 self::RELEASE_STATUS_DEV => esc_html__( 'Development', 'elementor' ),
415 426 self::RELEASE_STATUS_ALPHA => esc_html__( 'Alpha', 'elementor' ),
416 427 self::RELEASE_STATUS_BETA => esc_html__( 'Beta', 'elementor' ),
428 + self::RELEASE_STATUS_RC => esc_html__( 'Release Candidate', 'elementor' ),
417 429 self::RELEASE_STATUS_STABLE => esc_html__( 'Stable', 'elementor' ),
418 430 ];
419 431 }
420 432
@@ -438,8 +450,9 @@
438 450 * @param Settings $settings
439 451 *
440 452 * @since 3.1.0
441 453 * @access private
454 + *
442 455 */
443 456 private function register_settings_fields( Settings $settings ) {
444 457 $features = $this->get_features();
445 458
@@ -527,9 +540,9 @@
527 540 <?php echo esc_html__( 'Experiments and Features', 'elementor' ); ?>
528 541 </h2>
529 542 <p class="e-experiment__description">
530 543 <?php
531 - printf(
544 + echo sprintf(
532 545 /* translators: %1$s Link open tag, %2$s: Link close tag. */
533 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' ),
534 547 '<a href="https://go.elementor.com/wp-dash-experiments-report-an-issue/" target="_blank">',
535 548 '</a>'
@@ -537,9 +550,9 @@
537 550 ?>
538 551 </p>
539 552 <p class="e-experiment__description">
540 553 <?php
541 - printf(
554 + echo sprintf(
542 555 '%1$s <a href="https://go.elementor.com/wp-dash-experiments/" target="_blank">%2$s</a>',
543 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' ),
544 557 esc_html__( 'Learn more', 'elementor' ),
545 558 );
@@ -631,13 +644,13 @@
631 644 ->find( function ( $dependency ) {
632 645 return $dependency instanceof Non_Existing_Dependency;
633 646 } );
634 647
635 - return (bool) $non_existing_dep;
648 + return ! ! $non_existing_dep;
636 649 }
637 650
638 651 /**
639 - * Get Feature Settings Label HTML.
652 + * Get Feature Settings Label HTML
640 653 *
641 654 * @since 3.1.0
642 655 * @access private
643 656 *
@@ -662,12 +675,9 @@
662 675 <div class="e-experiment__title">
663 676 <div class="<?php echo $indicator_classes; ?>" data-tooltip="<?php echo $indicator_tooltip; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"></div>
664 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>
665 678 <?php foreach ( $feature['tags'] as $tag ) { ?>
666 - <?php
667 - $tag_classes = 'e-experiment__title__tag e-experiment__title__tag__' . $tag['type'] . ' e-editor-one';
668 - ?>
669 - <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>
670 680 <?php } ?>
671 681 <?php if ( $feature['deprecated'] ) { ?>
672 682 <span class="e-experiment__title__tag e-experiment__title__tag__deprecated"><?php echo esc_html__( 'Deprecated', 'elementor' ); ?></span>
673 683 <?php } ?>
@@ -735,11 +745,12 @@
735 745 *
736 746 * @since 3.1.0
737 747 * @access private
738 748 *
739 - * @param array $old_feature_data
749 + * @param array $old_feature_data
740 750 * @param string $new_state
741 - * @param string $old_state
751 + *
752 + * @throws \Elementor\Core\Experiments\Exceptions\Dependency_Exception
742 753 */
743 754 private function on_feature_state_change( array $old_feature_data, $new_state, $old_state ) {
744 755 $new_feature_data = $this->get_features( $old_feature_data['name'] );
745 756 $this->validate_dependency( $new_feature_data, $new_state );
@@ -756,9 +767,9 @@
756 767 do_action( 'elementor/experiments/feature-state-change/' . $old_feature_data['name'], $old_state, $new_state );
757 768 }
758 769
759 770 /**
760 - * @throws Exceptions\Dependency_Exception If the feature dependency is not available or not active.
771 + * @throws \Elementor\Core\Experiments\Exceptions\Dependency_Exception
761 772 */
762 773 private function validate_dependency( array $feature, $new_state ) {
763 774 $rollback = function ( $feature_option_key, $state ) {
764 775 remove_all_actions( 'add_option_' . $feature_option_key );
@@ -787,10 +798,10 @@
787 798
788 799 throw new Exceptions\Dependency_Exception(
789 800 sprintf(
790 801 'The feature `%s` has a dependency `%s` that is not available.',
791 - esc_html( $feature['name'] ),
792 - esc_html( $dependency->get_name() )
802 + $feature['name'],
803 + $dependency->get_name()
793 804 )
794 805 );
795 806 }
796 807
@@ -802,10 +813,10 @@
802 813
803 814 throw new Exceptions\Dependency_Exception(
804 815 sprintf(
805 816 'To turn on `%1$s`, Experiment: `%2$s` activity is required!',
806 - esc_html( $feature['name'] ),
807 - esc_html( $dependency_feature['name'] )
817 + $feature['name'],
818 + $dependency_feature['name']
808 819 )
809 820 );
810 821 }
811 822 }
@@ -850,9 +861,11 @@
850 861 * This behavior interferes with the experiments dependency mechanism because the data that's being sent can be in any order,
851 862 * while the dependencies mechanism expects it to be in a specific order (dependencies should be activated before their dependents can).
852 863 * In order to solve this issue, we sort the experiments in the POST data based on their dependencies tree.
853 864 *
854 - * @param array $allowed_options
865 + * @param $allowed_options
866 + *
867 + * @return mixed
855 868 */
856 869 private function sort_allowed_options_by_dependencies( $allowed_options ) {
857 870 if ( ! isset( $allowed_options['elementor'] ) ) {
858 871 return $allowed_options;
@@ -945,10 +958,9 @@
945 958
946 959 /**
947 960 * @param array $experimental_data
948 961 * @return array
949 - *
950 - * @throws Exceptions\Dependency_Exception If the feature dependency is not initialized or depends on a hidden experiment.
962 + * @throws Dependency_Exception
951 963 */
952 964 private function initialize_feature_dependencies( array $experimental_data ): array {
953 965 foreach ( $experimental_data['dependencies'] as $key => $dependency ) {
954 966 $feature = $this->get_features( $dependency );
@@ -954,15 +966,9 @@
954 966 $feature = $this->get_features( $dependency );
955 967
956 968 if ( ! isset( $feature ) ) {
957 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.
958 - throw new Exceptions\Dependency_Exception(
959 - sprintf(
960 - 'Feature %s cannot be initialized before dependency feature: %s.',
961 - esc_html( $experimental_data['name'] ),
962 - esc_html( $dependency )
963 - )
964 - );
970 + throw new Exceptions\Dependency_Exception( "Feature {$experimental_data['name']} cannot be initialized before dependency feature: {$dependency}." );
965 971 }
966 972
967 973 if ( ! empty( $feature[ static::TYPE_HIDDEN ] ) ) {
968 974 throw new Exceptions\Dependency_Exception( 'Depending on a hidden experiment is not allowed.' );
@@ -999,9 +1005,9 @@
999 1005 return $experimental_data;
1000 1006 }
1001 1007
1002 1008 /**
1003 - * @param array $new_site
1009 + * @param $new_site
1004 1010 * @param array $experimental_data
1005 1011 * @return array
1006 1012 */
1007 1013 private function set_new_site_default_state( $new_site, array $experimental_data ): array {