PluginProbe
Elementor Website Builder – more than just a page builder / 3.1.0-dev2
Elementor Website Builder – more than just a page builder v3.1.0-dev2
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 / experiments / manager.php

manager.php in Elementor Website Builder – more than just a page builder 3.1.0-dev2, at core/experiments/manager.php

451 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Core\Experiments;
4
5 use Elementor\Core\Base\Base_Object;
6 use Elementor\Plugin;
7 use Elementor\Tools;
8 use Elementor\Tracker;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly
12 }
13
14 class Manager extends Base_Object {
15
16 const RELEASE_STATUS_DEV = 'dev';
17
18 const RELEASE_STATUS_ALPHA = 'alpha';
19
20 const RELEASE_STATUS_BETA = 'beta';
21
22 const RELEASE_STATUS_RC = 'rc';
23
24 const RELEASE_STATUS_STABLE = 'stable';
25
26 const STATE_DEFAULT = 'default';
27
28 const STATE_ACTIVE = 'active';
29
30 const STATE_INACTIVE = 'inactive';
31
32 private $states;
33
34 private $release_statuses;
35
36 private $features;
37
38 /**
39 * Add Feature
40 *
41 * @since 3.1.0
42 * @access public
43 *
44 * @param array $options {
45 * @type string $name
46 * @type string $title
47 * @type string $description
48 * @type string $release_status
49 * @type string $default
50 * @type callable $on_state_change
51 * }
52 *
53 * @return array|null
54 */
55 public function add_feature( array $options ) {
56 if ( isset( $this->features[ $options['name'] ] ) ) {
57 return null;
58 }
59
60 $default_experimental_data = [
61 'description' => '',
62 'release_status' => self::RELEASE_STATUS_ALPHA,
63 'default' => self::STATE_INACTIVE,
64 'on_state_change' => null,
65 ];
66
67 $allowed_options = [ 'name', 'title', 'description', 'release_status', 'default', 'on_state_change' ];
68
69 $experimental_data = $this->merge_properties( $default_experimental_data, $options, $allowed_options );
70
71 $experimental_data = array_merge( $default_experimental_data, $experimental_data );
72
73 $state = $this->get_saved_feature_state( $options['name'] );
74
75 if ( ! $state ) {
76 $state = self::STATE_DEFAULT;
77 }
78
79 $experimental_data['state'] = $state;
80
81 $this->features[ $options['name'] ] = $experimental_data;
82
83 if ( is_admin() ) {
84 $feature_option_key = $this->get_feature_option_key( $options['name'] );
85
86 $on_state_change_callback = function( $old_state, $new_state ) use ( $experimental_data ) {
87 $this->on_feature_state_change( $experimental_data, $new_state );
88 };
89
90 add_action( 'add_option_' . $feature_option_key, $on_state_change_callback, 10, 2 );
91 add_action( 'update_option_' . $feature_option_key, $on_state_change_callback, 10, 2 );
92 }
93
94 do_action( 'elementor/experiments/feature-registered', $this, $experimental_data );
95
96 return $experimental_data;
97 }
98
99 /**
100 * Remove Feature
101 *
102 * @since 3.1.0
103 * @access public
104 *
105 * @param string $feature_name
106 */
107 public function remove_feature( $feature_name ) {
108 unset( $this->features[ $feature_name ] );
109 }
110
111 /**
112 * Get Features
113 *
114 * @since 3.1.0
115 * @access public
116 *
117 * @param string $feature_name Optional. Default is null
118 *
119 * @return array|null
120 */
121 public function get_features( $feature_name = null ) {
122 return self::get_items( $this->features, $feature_name );
123 }
124
125 /**
126 * Get Active Features
127 *
128 * @since 3.1.0
129 * @access public
130 *
131 * @return array
132 */
133 public function get_active_features() {
134 return array_filter( $this->features, [ $this, 'is_feature_active' ], ARRAY_FILTER_USE_KEY );
135 }
136
137 /**
138 * Is Feature Active
139 *
140 * @since 3.1.0
141 * @access public
142 *
143 * @param string $feature_name
144 *
145 * @return bool
146 */
147 public function is_feature_active( $feature_name ) {
148 $feature = $this->get_features( $feature_name );
149
150 if ( ! $feature ) {
151 return false;
152 }
153
154 return self::STATE_ACTIVE === $this->get_feature_actual_state( $feature );
155 }
156
157 /**
158 * Set Feature Default State
159 *
160 * @since 3.1.0
161 * @access public
162 *
163 * @param string $feature_name
164 * @param int $default_state
165 */
166 public function set_feature_default_state( $feature_name, $default_state ) {
167 $feature = $this->get_features( $feature_name );
168
169 if ( ! $feature ) {
170 return;
171 }
172
173 $this->features[ $feature_name ]['default'] = $default_state;
174 }
175
176 /**
177 * Get Feature Option Key
178 *
179 * @since 3.1.0
180 * @access private
181 *
182 * @param string $feature_name
183 *
184 * @return string
185 */
186 private function get_feature_option_key( $feature_name ) {
187 return 'elementor_experiment-' . $feature_name;
188 }
189
190 private function add_default_features() {
191 $this->add_feature( [
192 'name' => 'e_dom_optimization',
193 'title' => __( 'Optimized DOM Output', 'elementor' ),
194 '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' )
195 . ' <a href="https://go.elementor.com/wp-dash-legacy-optimized-dom" target="_blank">'
196 . __( 'Learn More', 'elementor' ) . '</a>',
197 'release_status' => self::RELEASE_STATUS_ALPHA,
198 ] );
199
200 $this->add_feature( [
201 'name' => 'a11y_improvements',
202 'title' => __( 'Accessibility Improvements', 'elementor' ),
203 'description' => __( 'An array of accessibility enhancements in Elementor pages.', 'elementor' )
204 . '<br><strong>' . __( 'Please note!', 'elementor' ) . '</strong> ' . __( 'These enhancements may include some markup changes to existing elementor widgets', 'elementor' )
205 . ' <a href="https://go.elementor.com/wp-dash-a11y-improvements" target="_blank">'
206 . __( 'Learn More', 'elementor' ) . '</a>',
207 'release_status' => self::RELEASE_STATUS_ALPHA,
208 ] );
209 }
210
211 /**
212 * Init States
213 *
214 * @since 3.1.0
215 * @access private
216 */
217 private function init_states() {
218 $this->states = [
219 self::STATE_DEFAULT => __( 'Default', 'elementor' ),
220 self::STATE_ACTIVE => __( 'Active', 'elementor' ),
221 self::STATE_INACTIVE => __( 'Inactive', 'elementor' ),
222 ];
223 }
224
225 /**
226 * Init Statuses
227 *
228 * @since 3.1.0
229 * @access private
230 */
231 private function init_release_statuses() {
232 $this->release_statuses = [
233 self::RELEASE_STATUS_DEV => __( 'Development', 'elementor' ),
234 self::RELEASE_STATUS_ALPHA => __( 'Alpha', 'elementor' ),
235 self::RELEASE_STATUS_BETA => __( 'Beta', 'elementor' ),
236 self::RELEASE_STATUS_RC => __( 'Release Candidate', 'elementor' ),
237 self::RELEASE_STATUS_STABLE => __( 'Stable', 'elementor' ),
238 ];
239 }
240
241 /**
242 * Init Features
243 *
244 * @since 3.1.0
245 * @access private
246 */
247 private function init_features() {
248 $this->features = [];
249
250 $this->add_default_features();
251
252 do_action( 'elementor/experiments/default-features-registered', $this );
253 }
254
255 /**
256 * Register Settings Fields
257 *
258 * @since 3.1.0
259 * @access private
260 *
261 * @param Tools $tools
262 */
263 private function register_settings_fields( Tools $tools ) {
264 $features = $this->get_features();
265
266 $fields = [];
267
268 foreach ( $features as $feature_name => $feature ) {
269 $feature_key = 'experiment-' . $feature_name;
270
271 $fields[ $feature_key ]['label'] = $this->get_feature_settings_label_html( $feature );
272
273 $fields[ $feature_key ]['field_args'] = $feature;
274
275 $fields[ $feature_key ]['render'] = function( $feature ) {
276 $this->render_feature_settings_field( $feature );
277 };
278 }
279
280 if ( ! $features ) {
281 $fields['no_features'] = [
282 'label' => __( 'No available experiments', 'elementor' ),
283 'field_args' => [
284 'type' => 'raw_html',
285 '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' ),
286 ],
287 ];
288 }
289
290 if ( ! Tracker::is_allow_track() ) {
291 $fields += $tools->get_usage_fields();
292 }
293
294 $tools->add_tab(
295 'experiments', [
296 'label' => __( 'Experiments', 'elementor' ),
297 'sections' => [
298 'experiments' => [
299 'callback' => function() {
300 $this->render_settings_intro();
301 },
302 'fields' => $fields,
303 ],
304 ],
305 ]
306 );
307 }
308
309 /**
310 * Render Settings Intro
311 *
312 * @since 3.1.0
313 * @access private
314 */
315 private function render_settings_intro() {
316 ?>
317 <h2><?php echo __( 'Elementor Experiments', 'elementor' ); ?></h2>
318 <p class="e-experiments__description"><?php echo sprintf( __( 'Access new and experimental features from Elementor before they\'re officially released. As these features are still in development, they are likely to change, evolve or even be removed altogether. <a href="%s" target="_blank">Learn More.</a>', 'elementor' ), 'https://go.elementor.com/wp-dash-experiments/' ); ?></p>
319 <p class="e-experiments__description"><?php echo __( 'To use an experiment on your site, simply click on the dropdown next to it and switch to Active. You can always deactivate them at any time.', 'elementor' ); ?></p>
320 <p class="e-experiments__description"><?php echo sprintf( __( 'Your feedback is important - <a href="%s" target="_blank">help us</a> improve these features by sharing your thoughts and inputs.', 'elementor' ), 'https://go.elementor.com/wp-dash-experiments-report-an-issue/' ); ?></p>
321 <?php
322 }
323
324 /**
325 * Render Feature Settings Field
326 *
327 * @since 3.1.0
328 * @access private
329 *
330 * @param array $feature
331 */
332 private function render_feature_settings_field( array $feature ) {
333 ?>
334 <div class="e-experiment__content">
335 <select id="e-experiment-<?php echo $feature['name']; ?>" class="e-experiment__select" name="<?php echo $this->get_feature_option_key( $feature['name'] ); ?>">
336 <?php foreach ( $this->states as $state_key => $state_title ) { ?>
337 <option value="<?php echo $state_key; ?>" <?php selected( $state_key, $feature['state'] ); ?>><?php echo $state_title; ?></option>
338 <?php } ?>
339 </select>
340 <div class="e-experiment__description"><?php echo $feature['description']; ?></div>
341 <div class="e-experiment__status"><?php echo sprintf( __( 'Status: %s', 'elementor' ), $this->release_statuses[ $feature['release_status'] ] ); ?></div>
342 </div>
343 <?php
344 }
345
346 /**
347 * Get Feature Settings Label HTML
348 *
349 * @since 3.1.0
350 * @access private
351 *
352 * @param array $feature
353 *
354 * @return string
355 */
356 private function get_feature_settings_label_html( array $feature ) {
357 ob_start();
358
359 $indicator_classes = 'e-experiment__title__indicator';
360
361 if ( $this->is_feature_active( $feature['name'] ) ) {
362 $indicator_classes .= ' e-experiment__title__indicator--active';
363 }
364 ?>
365 <div class="e-experiment__title">
366 <div class="<?php echo $indicator_classes; ?>"></div>
367 <label class="e-experiment__title__label" for="e-experiment-<?php echo $feature['name']; ?>"><?php echo $feature['title']; ?></label>
368 </div>
369 <?php
370
371 return ob_get_clean();
372 }
373
374 /**
375 * Get Feature Settings Label HTML
376 *
377 * @since 3.1.0
378 * @access private
379 *
380 * @param string $feature_name
381 *
382 * @return int
383 */
384 private function get_saved_feature_state( $feature_name ) {
385 return get_option( $this->get_feature_option_key( $feature_name ) );
386 }
387
388 /**
389 * Get Feature Actual State
390 *
391 * @since 3.1.0
392 * @access private
393 *
394 * @param array $feature
395 *
396 * @return string
397 */
398 private function get_feature_actual_state( array $feature ) {
399 if ( self::STATE_DEFAULT !== $feature['state'] ) {
400 return $feature['state'];
401 }
402
403 return $feature['default'];
404 }
405
406 /**
407 * On Feature State Change
408 *
409 * @since 3.1.0
410 * @access private
411 *
412 * @param array $old_feature_data
413 * @param string $new_state
414 */
415 private function on_feature_state_change( array $old_feature_data, $new_state ) {
416 $this->features[ $old_feature_data['name'] ]['state'] = $new_state;
417
418 $new_feature_data = $this->get_features( $old_feature_data['name'] );
419
420 $actual_old_state = $this->get_feature_actual_state( $old_feature_data );
421
422 $actual_new_state = $this->get_feature_actual_state( $new_feature_data );
423
424 if ( $actual_old_state === $actual_new_state ) {
425 return;
426 }
427
428 Plugin::$instance->files_manager->clear_cache();
429
430 if ( $new_feature_data['on_state_change'] ) {
431 $new_feature_data['on_state_change']( $actual_old_state, $actual_new_state );
432 }
433 }
434
435 public function __construct() {
436 $this->init_states();
437
438 $this->init_release_statuses();
439
440 $this->init_features();
441
442 if ( is_admin() ) {
443 $page_id = Tools::PAGE_ID;
444
445 add_action( "elementor/admin/after_create_settings/{$page_id}", function( Tools $tools ) {
446 $this->register_settings_fields( $tools );
447 }, 11 );
448 }
449 }
450 }
451