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 +76 -62 4.1.13.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,20 +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 +
383 + $this->add_feature( [
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' ),
396 + 'tag' => esc_html__( 'Performance', 'elementor' ),
397 + 'release_status' => self::RELEASE_STATUS_BETA,
398 + 'generator_tag' => true,
399 + 'default' => self::STATE_INACTIVE,
400 + ] );
382 401 }
383 402
384 403 /**
385 404 * Init States
@@ -405,8 +424,9 @@
405 424 $this->release_statuses = [
406 425 self::RELEASE_STATUS_DEV => esc_html__( 'Development', 'elementor' ),
407 426 self::RELEASE_STATUS_ALPHA => esc_html__( 'Alpha', 'elementor' ),
408 427 self::RELEASE_STATUS_BETA => esc_html__( 'Beta', 'elementor' ),
428 + self::RELEASE_STATUS_RC => esc_html__( 'Release Candidate', 'elementor' ),
409 429 self::RELEASE_STATUS_STABLE => esc_html__( 'Stable', 'elementor' ),
410 430 ];
411 431 }
412 432
@@ -430,8 +450,9 @@
430 450 * @param Settings $settings
431 451 *
432 452 * @since 3.1.0
433 453 * @access private
454 + *
434 455 */
435 456 private function register_settings_fields( Settings $settings ) {
436 457 $features = $this->get_features();
437 458
@@ -519,9 +540,9 @@
519 540 <?php echo esc_html__( 'Experiments and Features', 'elementor' ); ?>
520 541 </h2>
521 542 <p class="e-experiment__description">
522 543 <?php
523 - printf(
544 + echo sprintf(
524 545 /* translators: %1$s Link open tag, %2$s: Link close tag. */
525 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' ),
526 547 '<a href="https://go.elementor.com/wp-dash-experiments-report-an-issue/" target="_blank">',
527 548 '</a>'
@@ -529,9 +550,9 @@
529 550 ?>
530 551 </p>
531 552 <p class="e-experiment__description">
532 553 <?php
533 - printf(
554 + echo sprintf(
534 555 '%1$s <a href="https://go.elementor.com/wp-dash-experiments/" target="_blank">%2$s</a>',
535 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' ),
536 557 esc_html__( 'Learn more', 'elementor' ),
537 558 );
@@ -623,13 +644,13 @@
623 644 ->find( function ( $dependency ) {
624 645 return $dependency instanceof Non_Existing_Dependency;
625 646 } );
626 647
627 - return (bool) $non_existing_dep;
648 + return ! ! $non_existing_dep;
628 649 }
629 650
630 651 /**
631 - * Get Feature Settings Label HTML.
652 + * Get Feature Settings Label HTML
632 653 *
633 654 * @since 3.1.0
634 655 * @access private
635 656 *
@@ -654,12 +675,9 @@
654 675 <div class="e-experiment__title">
655 676 <div class="<?php echo $indicator_classes; ?>" data-tooltip="<?php echo $indicator_tooltip; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>"></div>
656 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>
657 678 <?php foreach ( $feature['tags'] as $tag ) { ?>
658 - <?php
659 - $tag_classes = 'e-experiment__title__tag e-experiment__title__tag__' . $tag['type'] . ' e-editor-one';
660 - ?>
661 - <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>
662 680 <?php } ?>
663 681 <?php if ( $feature['deprecated'] ) { ?>
664 682 <span class="e-experiment__title__tag e-experiment__title__tag__deprecated"><?php echo esc_html__( 'Deprecated', 'elementor' ); ?></span>
665 683 <?php } ?>
@@ -727,11 +745,12 @@
727 745 *
728 746 * @since 3.1.0
729 747 * @access private
730 748 *
731 - * @param array $old_feature_data
749 + * @param array $old_feature_data
732 750 * @param string $new_state
733 - * @param string $old_state
751 + *
752 + * @throws \Elementor\Core\Experiments\Exceptions\Dependency_Exception
734 753 */
735 754 private function on_feature_state_change( array $old_feature_data, $new_state, $old_state ) {
736 755 $new_feature_data = $this->get_features( $old_feature_data['name'] );
737 756 $this->validate_dependency( $new_feature_data, $new_state );
@@ -748,9 +767,9 @@
748 767 do_action( 'elementor/experiments/feature-state-change/' . $old_feature_data['name'], $old_state, $new_state );
749 768 }
750 769
751 770 /**
752 - * @throws Exceptions\Dependency_Exception If the feature dependency is not available or not active.
771 + * @throws \Elementor\Core\Experiments\Exceptions\Dependency_Exception
753 772 */
754 773 private function validate_dependency( array $feature, $new_state ) {
755 774 $rollback = function ( $feature_option_key, $state ) {
756 775 remove_all_actions( 'add_option_' . $feature_option_key );
@@ -779,10 +798,10 @@
779 798
780 799 throw new Exceptions\Dependency_Exception(
781 800 sprintf(
782 801 'The feature `%s` has a dependency `%s` that is not available.',
783 - esc_html( $feature['name'] ),
784 - esc_html( $dependency->get_name() )
802 + $feature['name'],
803 + $dependency->get_name()
785 804 )
786 805 );
787 806 }
788 807
@@ -794,10 +813,10 @@
794 813
795 814 throw new Exceptions\Dependency_Exception(
796 815 sprintf(
797 816 'To turn on `%1$s`, Experiment: `%2$s` activity is required!',
798 - esc_html( $feature['name'] ),
799 - esc_html( $dependency_feature['name'] )
817 + $feature['name'],
818 + $dependency_feature['name']
800 819 )
801 820 );
802 821 }
803 822 }
@@ -842,9 +861,11 @@
842 861 * This behavior interferes with the experiments dependency mechanism because the data that's being sent can be in any order,
843 862 * while the dependencies mechanism expects it to be in a specific order (dependencies should be activated before their dependents can).
844 863 * In order to solve this issue, we sort the experiments in the POST data based on their dependencies tree.
845 864 *
846 - * @param array $allowed_options
865 + * @param $allowed_options
866 + *
867 + * @return mixed
847 868 */
848 869 private function sort_allowed_options_by_dependencies( $allowed_options ) {
849 870 if ( ! isset( $allowed_options['elementor'] ) ) {
850 871 return $allowed_options;
@@ -937,10 +958,9 @@
937 958
938 959 /**
939 960 * @param array $experimental_data
940 961 * @return array
941 - *
942 - * @throws Exceptions\Dependency_Exception If the feature dependency is not initialized or depends on a hidden experiment.
962 + * @throws Dependency_Exception
943 963 */
944 964 private function initialize_feature_dependencies( array $experimental_data ): array {
945 965 foreach ( $experimental_data['dependencies'] as $key => $dependency ) {
946 966 $feature = $this->get_features( $dependency );
@@ -946,15 +966,9 @@
946 966 $feature = $this->get_features( $dependency );
947 967
948 968 if ( ! isset( $feature ) ) {
949 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.
950 - throw new Exceptions\Dependency_Exception(
951 - sprintf(
952 - 'Feature %s cannot be initialized before dependency feature: %s.',
953 - esc_html( $experimental_data['name'] ),
954 - esc_html( $dependency )
955 - )
956 - );
970 + throw new Exceptions\Dependency_Exception( "Feature {$experimental_data['name']} cannot be initialized before dependency feature: {$dependency}." );
957 971 }
958 972
959 973 if ( ! empty( $feature[ static::TYPE_HIDDEN ] ) ) {
960 974 throw new Exceptions\Dependency_Exception( 'Depending on a hidden experiment is not allowed.' );
@@ -991,9 +1005,9 @@
991 1005 return $experimental_data;
992 1006 }
993 1007
994 1008 /**
995 - * @param array $new_site
1009 + * @param $new_site
996 1010 * @param array $experimental_data
997 1011 * @return array
998 1012 */
999 1013 private function set_new_site_default_state( $new_site, array $experimental_data ): array {