features[ $options['name'] ] ) ) { return null; } $default_experimental_data = [ 'description' => '', 'release_status' => self::RELEASE_STATUS_ALPHA, 'default' => self::STATE_INACTIVE, 'on_state_change' => null, ]; $allowed_options = [ 'name', 'title', 'description', 'release_status', 'default', 'on_state_change' ]; $experimental_data = $this->merge_properties( $default_experimental_data, $options, $allowed_options ); $experimental_data = array_merge( $default_experimental_data, $experimental_data ); $state = $this->get_saved_feature_state( $options['name'] ); if ( ! $state ) { $state = self::STATE_DEFAULT; } $experimental_data['state'] = $state; $this->features[ $options['name'] ] = $experimental_data; if ( is_admin() ) { $feature_option_key = $this->get_feature_option_key( $options['name'] ); $on_state_change_callback = function( $old_state, $new_state ) use ( $experimental_data ) { $this->on_feature_state_change( $experimental_data, $new_state ); }; add_action( 'add_option_' . $feature_option_key, $on_state_change_callback, 10, 2 ); add_action( 'update_option_' . $feature_option_key, $on_state_change_callback, 10, 2 ); } do_action( 'elementor/experiments/feature-registered', $this, $experimental_data ); return $experimental_data; } /** * Remove Feature * * @since 3.1.0 * @access public * * @param string $feature_name */ public function remove_feature( $feature_name ) { unset( $this->features[ $feature_name ] ); } /** * Get Features * * @since 3.1.0 * @access public * * @param string $feature_name Optional. Default is null * * @return array|null */ public function get_features( $feature_name = null ) { return self::get_items( $this->features, $feature_name ); } /** * Get Active Features * * @since 3.1.0 * @access public * * @return array */ public function get_active_features() { return array_filter( $this->features, [ $this, 'is_feature_active' ], ARRAY_FILTER_USE_KEY ); } /** * Is Feature Active * * @since 3.1.0 * @access public * * @param string $feature_name * * @return bool */ public function is_feature_active( $feature_name ) { $feature = $this->get_features( $feature_name ); if ( ! $feature ) { return false; } return self::STATE_ACTIVE === $this->get_feature_actual_state( $feature ); } /** * Set Feature Default State * * @since 3.1.0 * @access public * * @param string $feature_name * @param int $default_state */ public function set_feature_default_state( $feature_name, $default_state ) { $feature = $this->get_features( $feature_name ); if ( ! $feature ) { return; } $this->features[ $feature_name ]['default'] = $default_state; } /** * Get Feature Option Key * * @since 3.1.0 * @access private * * @param string $feature_name * * @return string */ private function get_feature_option_key( $feature_name ) { return 'elementor_experiment-' . $feature_name; } private function add_default_features() { $this->add_feature( [ 'name' => 'e_dom_optimization', 'title' => __( 'Optimized DOM Output', 'elementor' ), 'description' => __( 'Developers, Please Note! If you\'ve used custom code in Elementor, you might have experienced a snippet of code not running. Legacy DOM Output allows you to keep prior Elementor markup output settings, and have that lovely code running again.', 'elementor' ) . ' ' . __( 'Learn More', 'elementor' ) . '', 'release_status' => self::RELEASE_STATUS_ALPHA, ] ); $this->add_feature( [ 'name' => 'a11y_improvements', 'title' => __( 'Accessibility Improvements', 'elementor' ), 'description' => __( 'An array of accessibility enhancements in Elementor pages.', 'elementor' ) . '
' . __( 'Please note!', 'elementor' ) . ' ' . __( 'These enhancements may include some markup changes to existing elementor widgets', 'elementor' ) . ' ' . __( 'Learn More', 'elementor' ) . '', 'release_status' => self::RELEASE_STATUS_ALPHA, ] ); } /** * Init States * * @since 3.1.0 * @access private */ private function init_states() { $this->states = [ self::STATE_DEFAULT => __( 'Default', 'elementor' ), self::STATE_ACTIVE => __( 'Active', 'elementor' ), self::STATE_INACTIVE => __( 'Inactive', 'elementor' ), ]; } /** * Init Statuses * * @since 3.1.0 * @access private */ private function init_release_statuses() { $this->release_statuses = [ self::RELEASE_STATUS_DEV => __( 'Development', 'elementor' ), self::RELEASE_STATUS_ALPHA => __( 'Alpha', 'elementor' ), self::RELEASE_STATUS_BETA => __( 'Beta', 'elementor' ), self::RELEASE_STATUS_RC => __( 'Release Candidate', 'elementor' ), self::RELEASE_STATUS_STABLE => __( 'Stable', 'elementor' ), ]; } /** * Init Features * * @since 3.1.0 * @access private */ private function init_features() { $this->features = []; $this->add_default_features(); do_action( 'elementor/experiments/default-features-registered', $this ); } /** * Register Settings Fields * * @since 3.1.0 * @access private * * @param Tools $tools */ private function register_settings_fields( Tools $tools ) { $features = $this->get_features(); $fields = []; foreach ( $features as $feature_name => $feature ) { $feature_key = 'experiment-' . $feature_name; $fields[ $feature_key ]['label'] = $this->get_feature_settings_label_html( $feature ); $fields[ $feature_key ]['field_args'] = $feature; $fields[ $feature_key ]['render'] = function( $feature ) { $this->render_feature_settings_field( $feature ); }; } if ( ! $features ) { $fields['no_features'] = [ 'label' => __( 'No available experiments', 'elementor' ), 'field_args' => [ 'type' => 'raw_html', 'html' => __( 'The current version of Elementor doesn\'t have any experimental features . if you\'re feeling curious make sure to come back in future versions.', 'elementor' ), ], ]; } if ( ! Tracker::is_allow_track() ) { $fields += $tools->get_usage_fields(); } $tools->add_tab( 'experiments', [ 'label' => __( 'Experiments', 'elementor' ), 'sections' => [ 'experiments' => [ 'callback' => function() { $this->render_settings_intro(); }, 'fields' => $fields, ], ], ] ); } /** * Render Settings Intro * * @since 3.1.0 * @access private */ private function render_settings_intro() { ?>

Learn More.', 'elementor' ), 'https://go.elementor.com/wp-dash-experiments/' ); ?>

help us improve these features by sharing your thoughts and inputs.', 'elementor' ), 'https://go.elementor.com/wp-dash-experiments-report-an-issue/' ); ?>

release_statuses[ $feature['release_status'] ] ); ?>
is_feature_active( $feature['name'] ) ) { $indicator_classes .= ' e-experiment__title__indicator--active'; } ?>
get_feature_option_key( $feature_name ) ); } /** * Get Feature Actual State * * @since 3.1.0 * @access private * * @param array $feature * * @return string */ private function get_feature_actual_state( array $feature ) { if ( self::STATE_DEFAULT !== $feature['state'] ) { return $feature['state']; } return $feature['default']; } /** * On Feature State Change * * @since 3.1.0 * @access private * * @param array $old_feature_data * @param string $new_state */ private function on_feature_state_change( array $old_feature_data, $new_state ) { $this->features[ $old_feature_data['name'] ]['state'] = $new_state; $new_feature_data = $this->get_features( $old_feature_data['name'] ); $actual_old_state = $this->get_feature_actual_state( $old_feature_data ); $actual_new_state = $this->get_feature_actual_state( $new_feature_data ); if ( $actual_old_state === $actual_new_state ) { return; } Plugin::$instance->files_manager->clear_cache(); if ( $new_feature_data['on_state_change'] ) { $new_feature_data['on_state_change']( $actual_old_state, $actual_new_state ); } } public function __construct() { $this->init_states(); $this->init_release_statuses(); $this->init_features(); if ( is_admin() ) { $page_id = Tools::PAGE_ID; add_action( "elementor/admin/after_create_settings/{$page_id}", function( Tools $tools ) { $this->register_settings_fields( $tools ); }, 11 ); } } }