PluginProbe
Elementor Website Builder – more than just a page builder / 3.2.3
Elementor Website Builder – more than just a page builder v3.2.3
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 / files / css / base.php

base.php in Elementor Website Builder – more than just a page builder 3.2.3, at core/files/css/base.php

909 lines 23.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor\Core\Files\CSS;
3
4 use Elementor\Base_Data_Control;
5 use Elementor\Control_Repeater;
6 use Elementor\Controls_Manager;
7 use Elementor\Controls_Stack;
8 use Elementor\Core\Files\Base as Base_File;
9 use Elementor\Core\DynamicTags\Manager;
10 use Elementor\Core\DynamicTags\Tag;
11 use Elementor\Core\Kits\Documents\Tabs\Global_Typography;
12 use Elementor\Element_Base;
13 use Elementor\Plugin;
14 use Elementor\Stylesheet;
15 use Elementor\Icons_Manager;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit; // Exit if accessed directly.
19 }
20
21 /**
22 * Elementor CSS file.
23 *
24 * Elementor CSS file handler class is responsible for generating CSS files.
25 *
26 * @since 1.2.0
27 * @abstract
28 */
29 abstract class Base extends Base_File {
30
31 /**
32 * Elementor CSS file generated status.
33 *
34 * The parsing result after generating CSS file.
35 */
36 const CSS_STATUS_FILE = 'file';
37
38 /**
39 * Elementor inline CSS status.
40 *
41 * The parsing result after generating inline CSS.
42 */
43 const CSS_STATUS_INLINE = 'inline';
44
45 /**
46 * Elementor CSS empty status.
47 *
48 * The parsing result when an empty CSS returned.
49 */
50 const CSS_STATUS_EMPTY = 'empty';
51
52 /**
53 * Fonts.
54 *
55 * Holds the list of fonts.
56 *
57 * @access private
58 *
59 * @var array
60 */
61 private $fonts = [];
62
63 private $icons_fonts = [];
64
65 private $dynamic_elements_ids = [];
66
67 /**
68 * Stylesheet object.
69 *
70 * Holds the CSS file stylesheet instance.
71 *
72 * @access protected
73 *
74 * @var Stylesheet
75 */
76 protected $stylesheet_obj;
77
78 /**
79 * Printed.
80 *
81 * Holds the list of printed files.
82 *
83 * @access protected
84 *
85 * @var array
86 */
87 private static $printed = [];
88
89 /**
90 * Get CSS file name.
91 *
92 * Retrieve the CSS file name.
93 *
94 * @since 1.6.0
95 * @access public
96 * @abstract
97 */
98 abstract public function get_name();
99
100 protected function is_global_parsing_supported() {
101 return false;
102 }
103
104 /**
105 * Use external file.
106 *
107 * Whether to use external CSS file of not. When there are new schemes or settings
108 * updates.
109 *
110 * @since 1.9.0
111 * @access protected
112 *
113 * @return bool True if the CSS requires an update, False otherwise.
114 */
115 protected function use_external_file() {
116 return 'internal' !== get_option( 'elementor_css_print_method' );
117 }
118
119 /**
120 * Update the CSS file.
121 *
122 * Delete old CSS, parse the CSS, save the new file and update the database.
123 *
124 * This method also sets the CSS status to be used later on in the render posses.
125 *
126 * @since 1.2.0
127 * @access public
128 */
129 public function update() {
130 $this->update_file();
131
132 $meta = $this->get_meta();
133
134 $meta['time'] = time();
135
136 $content = $this->get_content();
137
138 if ( empty( $content ) ) {
139 $meta['status'] = self::CSS_STATUS_EMPTY;
140 $meta['css'] = '';
141 } else {
142 $use_external_file = $this->use_external_file();
143
144 if ( $use_external_file ) {
145 $meta['status'] = self::CSS_STATUS_FILE;
146 } else {
147 $meta['status'] = self::CSS_STATUS_INLINE;
148 $meta['css'] = $content;
149 }
150 }
151
152 $meta['dynamic_elements_ids'] = $this->dynamic_elements_ids;
153
154 $this->update_meta( $meta );
155 }
156
157 /**
158 * @since 2.1.0
159 * @access public
160 */
161 public function write() {
162 if ( $this->use_external_file() ) {
163 parent::write();
164 }
165 }
166
167 /**
168 * @since 3.0.0
169 * @access public
170 */
171 public function delete() {
172 if ( $this->use_external_file() ) {
173 parent::delete();
174 } else {
175 $this->delete_meta();
176 }
177 }
178
179 /**
180 * Enqueue CSS.
181 *
182 * Either enqueue the CSS file in Elementor or add inline style.
183 *
184 * This method is also responsible for loading the fonts.
185 *
186 * @since 1.2.0
187 * @access public
188 */
189 public function enqueue() {
190 $handle_id = $this->get_file_handle_id();
191
192 if ( isset( self::$printed[ $handle_id ] ) ) {
193 return;
194 }
195
196 self::$printed[ $handle_id ] = true;
197
198 $meta = $this->get_meta();
199
200 if ( self::CSS_STATUS_EMPTY === $meta['status'] ) {
201 return;
202 }
203
204 // First time after clear cache and etc.
205 if ( '' === $meta['status'] || $this->is_update_required() ) {
206 $this->update();
207
208 $meta = $this->get_meta();
209 }
210
211 if ( self::CSS_STATUS_INLINE === $meta['status'] ) {
212 $dep = $this->get_inline_dependency();
213 // If the dependency has already been printed ( like a template in footer )
214 if ( wp_styles()->query( $dep, 'done' ) ) {
215 printf( '<style id="%1$s">%2$s</style>', $this->get_file_handle_id(), $meta['css'] ); // XSS ok.
216 } else {
217 wp_add_inline_style( $dep, $meta['css'] );
218 }
219 } elseif ( self::CSS_STATUS_FILE === $meta['status'] ) { // Re-check if it's not empty after CSS update.
220 wp_enqueue_style( $this->get_file_handle_id(), $this->get_url(), $this->get_enqueue_dependencies(), null ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
221 }
222
223 // Handle fonts.
224 if ( ! empty( $meta['fonts'] ) ) {
225 foreach ( $meta['fonts'] as $font ) {
226 Plugin::$instance->frontend->enqueue_font( $font );
227 }
228 }
229
230 if ( ! empty( $meta['icons'] ) ) {
231 $icons_types = Icons_Manager::get_icon_manager_tabs();
232 foreach ( $meta['icons'] as $icon_font ) {
233 if ( ! isset( $icons_types[ $icon_font ] ) ) {
234 continue;
235 }
236 Plugin::$instance->frontend->enqueue_font( $icon_font );
237 }
238 }
239
240 $name = $this->get_name();
241
242 /**
243 * Enqueue CSS file.
244 *
245 * Fires when CSS file is enqueued on Elementor.
246 *
247 * The dynamic portion of the hook name, `$name`, refers to the CSS file name.
248 *
249 * @since 2.0.0
250 *
251 * @param Base $this The current CSS file.
252 */
253 do_action( "elementor/css-file/{$name}/enqueue", $this );
254 }
255
256 /**
257 * Print CSS.
258 *
259 * Output the final CSS inside the `<style>` tags and all the frontend fonts in
260 * use.
261 *
262 * @since 1.9.4
263 * @access public
264 */
265 public function print_css() {
266 echo '<style>' . $this->get_content() . '</style>'; // XSS ok.
267 Plugin::$instance->frontend->print_fonts_links();
268 }
269
270 /**
271 * Add control rules.
272 *
273 * Parse the CSS for all the elements inside any given control.
274 *
275 * This method recursively renders the CSS for all the selectors in the control.
276 *
277 * @since 1.2.0
278 * @access public
279 *
280 * @param array $control The controls.
281 * @param array $controls_stack The controls stack.
282 * @param callable $value_callback Callback function for the value.
283 * @param array $placeholders Placeholders.
284 * @param array $replacements Replacements.
285 * @param array $values Global Values.
286 */
287 public function add_control_rules( array $control, array $controls_stack, callable $value_callback, array $placeholders, array $replacements, array $values = [] ) {
288 if ( empty( $control['selectors'] ) ) {
289 return;
290 }
291
292 $control_global_key = $control['name'];
293
294 if ( ! empty( $control['groupType'] ) ) {
295 $control_global_key = $control['groupPrefix'] . $control['groupType'];
296 }
297
298 $global_values = [];
299 $global_key = '';
300
301 if ( ! empty( $values['__globals__'] ) ) {
302 $global_values = $values['__globals__'];
303 }
304
305 if ( ! empty( $global_values[ $control_global_key ] ) ) {
306 $global_key = $global_values[ $control_global_key ];
307 }
308
309 if ( ! $global_key ) {
310 $value = call_user_func( $value_callback, $control );
311
312 if ( null === $value ) {
313 return;
314 }
315 }
316
317 $stylesheet = $this->get_stylesheet();
318
319 foreach ( $control['selectors'] as $selector => $css_property ) {
320 $output_css_property = '';
321
322 if ( $global_key ) {
323 $selector_global_value = $this->get_selector_global_value( $control, $global_key );
324
325 if ( $selector_global_value ) {
326 $output_css_property = preg_replace( '/(:)[^;]+(;?)/', '$1' . $selector_global_value . '$2', $css_property );
327 }
328 } else {
329 try {
330 $output_css_property = preg_replace_callback( '/{{(?:([^.}]+)\.)?([^}| ]*)(?: *\|\| *(?:([^.}]+)\.)?([^}| ]*) *)*}}/', function( $matches ) use ( $control, $value_callback, $controls_stack, $value, $css_property ) {
331 $external_control_missing = $matches[1] && ! isset( $controls_stack[ $matches[1] ] );
332
333 $parsed_value = '';
334
335 if ( ! $external_control_missing ) {
336 $parsed_value = $this->parse_property_placeholder( $control, $value, $controls_stack, $value_callback, $matches[2], $matches[1] );
337 }
338
339 if ( '' === $parsed_value ) {
340 if ( isset( $matches[4] ) ) {
341 $parsed_value = $matches[4];
342
343 $is_string_value = preg_match( '/^([\'"])(.*)\1$/', $parsed_value, $string_matches );
344
345 if ( $is_string_value ) {
346 $parsed_value = $string_matches[2];
347 } elseif ( ! is_numeric( $parsed_value ) ) {
348 if ( $matches[3] && ! isset( $controls_stack[ $matches[3] ] ) ) {
349 return '';
350 }
351
352 $parsed_value = $this->parse_property_placeholder( $control, $value, $controls_stack, $value_callback, $matches[4], $matches[3] );
353 }
354 }
355
356 if ( '' === $parsed_value ) {
357 if ( $external_control_missing ) {
358 return '';
359 }
360
361 throw new \Exception();
362 }
363 }
364
365 return $parsed_value;
366 }, $css_property );
367 } catch ( \Exception $e ) {
368 return;
369 }
370 }
371
372 if ( ! $output_css_property ) {
373 continue;
374 }
375
376 $device_pattern = '/^(?:\([^\)]+\)){1,2}/';
377
378 preg_match( $device_pattern, $selector, $device_rules );
379
380 $query = [];
381
382 if ( $device_rules ) {
383 $selector = preg_replace( $device_pattern, '', $selector );
384
385 preg_match_all( '/\(([^)]+)\)/', $device_rules[0], $pure_device_rules );
386
387 $pure_device_rules = $pure_device_rules[1];
388
389 foreach ( $pure_device_rules as $device_rule ) {
390 if ( Element_Base::RESPONSIVE_DESKTOP === $device_rule ) {
391 continue;
392 }
393
394 $device = preg_replace( '/\+$/', '', $device_rule );
395
396 $endpoint = $device === $device_rule ? 'max' : 'min';
397
398 $query[ $endpoint ] = $device;
399 }
400 }
401
402 $parsed_selector = str_replace( $placeholders, $replacements, $selector );
403
404 if ( ! $query && ! empty( $control['responsive'] ) ) {
405 $query = array_intersect_key( $control['responsive'], array_flip( [ 'min', 'max' ] ) );
406
407 if ( ! empty( $query['max'] ) && Element_Base::RESPONSIVE_DESKTOP === $query['max'] ) {
408 unset( $query['max'] );
409 }
410 }
411
412 $stylesheet->add_rules( $parsed_selector, $output_css_property, $query );
413 }
414 }
415
416 /**
417 * @param array $control
418 * @param mixed $value
419 * @param array $controls_stack
420 * @param callable $value_callback
421 * @param string $placeholder
422 * @param string $parser_control_name
423 *
424 * @return string
425 */
426 public function parse_property_placeholder( array $control, $value, array $controls_stack, $value_callback, $placeholder, $parser_control_name = null ) {
427 if ( $parser_control_name ) {
428 $control = $controls_stack[ $parser_control_name ];
429
430 $value = call_user_func( $value_callback, $control );
431 }
432
433 if ( Controls_Manager::FONT === $control['type'] ) {
434 $this->fonts[] = $value;
435 }
436
437 /** @var Base_Data_Control $control_obj */
438 $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] );
439
440 return (string) $control_obj->get_style_value( $placeholder, $value, $control );
441 }
442
443 /**
444 * Get the fonts.
445 *
446 * Retrieve the list of fonts.
447 *
448 * @since 1.9.0
449 * @access public
450 *
451 * @return array Fonts.
452 */
453 public function get_fonts() {
454 return $this->fonts;
455 }
456
457 /**
458 * Get stylesheet.
459 *
460 * Retrieve the CSS file stylesheet instance.
461 *
462 * @since 1.2.0
463 * @access public
464 *
465 * @return Stylesheet The stylesheet object.
466 */
467 public function get_stylesheet() {
468 if ( ! $this->stylesheet_obj ) {
469 $this->init_stylesheet();
470 }
471
472 return $this->stylesheet_obj;
473 }
474
475 /**
476 * Add controls stack style rules.
477 *
478 * Parse the CSS for all the elements inside any given controls stack.
479 *
480 * This method recursively renders the CSS for all the child elements in the stack.
481 *
482 * @since 1.6.0
483 * @access public
484 *
485 * @param Controls_Stack $controls_stack The controls stack.
486 * @param array $controls Controls array.
487 * @param array $values Values array.
488 * @param array $placeholders Placeholders.
489 * @param array $replacements Replacements.
490 * @param array $all_controls All controls.
491 */
492 public function add_controls_stack_style_rules( Controls_Stack $controls_stack, array $controls, array $values, array $placeholders, array $replacements, array $all_controls = null ) {
493 if ( ! $all_controls ) {
494 $all_controls = $controls_stack->get_controls();
495 }
496
497 $parsed_dynamic_settings = $controls_stack->parse_dynamic_settings( $values, $controls );
498
499 foreach ( $controls as $control ) {
500 if ( ! empty( $control['style_fields'] ) ) {
501 $this->add_repeater_control_style_rules( $controls_stack, $control, $values[ $control['name'] ], $placeholders, $replacements );
502 }
503
504 if ( ! empty( $control[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ) ) {
505 $this->add_dynamic_control_style_rules( $control, $control[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] );
506 }
507
508 if ( Controls_Manager::ICONS === $control['type'] ) {
509 $this->icons_fonts[] = $values[ $control['name'] ]['library'];
510 }
511
512 if ( ! empty( $parsed_dynamic_settings[ Manager::DYNAMIC_SETTING_KEY ][ $control['name'] ] ) ) {
513 // Dynamic CSS should not be added to the CSS files.
514 // Instead it's handled by \Elementor\Core\DynamicTags\Dynamic_CSS
515 // and printed in a style tag.
516 unset( $parsed_dynamic_settings[ $control['name'] ] );
517
518 $this->dynamic_elements_ids[] = $controls_stack->get_id();
519
520 continue;
521 }
522
523 if ( empty( $control['selectors'] ) ) {
524 continue;
525 }
526
527 $this->add_control_style_rules( $control, $parsed_dynamic_settings, $all_controls, $placeholders, $replacements );
528 }
529 }
530
531 /**
532 * Get file handle ID.
533 *
534 * Retrieve the file handle ID.
535 *
536 * @since 1.2.0
537 * @access protected
538 * @abstract
539 *
540 * @return string CSS file handle ID.
541 */
542 abstract protected function get_file_handle_id();
543
544 /**
545 * Render CSS.
546 *
547 * Parse the CSS.
548 *
549 * @since 1.2.0
550 * @access protected
551 * @abstract
552 */
553 abstract protected function render_css();
554
555 protected function get_default_meta() {
556 return array_merge( parent::get_default_meta(), [
557 'fonts' => array_unique( $this->fonts ),
558 'icons' => array_unique( $this->icons_fonts ),
559 'dynamic_elements_ids' => [],
560 'status' => '',
561 ] );
562 }
563
564 /**
565 * Get enqueue dependencies.
566 *
567 * Retrieve the name of the stylesheet used by `wp_enqueue_style()`.
568 *
569 * @since 1.2.0
570 * @access protected
571 *
572 * @return array Name of the stylesheet.
573 */
574 protected function get_enqueue_dependencies() {
575 return [];
576 }
577
578 /**
579 * Get inline dependency.
580 *
581 * Retrieve the name of the stylesheet used by `wp_add_inline_style()`.
582 *
583 * @since 1.2.0
584 * @access protected
585 *
586 * @return string Name of the stylesheet.
587 */
588 protected function get_inline_dependency() {
589 return '';
590 }
591
592 /**
593 * Is update required.
594 *
595 * Whether the CSS requires an update. When there are new schemes or settings
596 * updates.
597 *
598 * @since 1.2.0
599 * @access protected
600 *
601 * @return bool True if the CSS requires an update, False otherwise.
602 */
603 protected function is_update_required() {
604 return false;
605 }
606
607 /**
608 * Parse CSS.
609 *
610 * Parsing the CSS file.
611 *
612 * @since 1.2.0
613 * @access protected
614 */
615 protected function parse_content() {
616 $this->render_css();
617
618 $name = $this->get_name();
619
620 /**
621 * Parse CSS file.
622 *
623 * Fires when CSS file is parsed on Elementor.
624 *
625 * The dynamic portion of the hook name, `$name`, refers to the CSS file name.
626 *
627 * @since 2.0.0
628 *
629 * @param Base $this The current CSS file.
630 */
631 do_action( "elementor/css-file/{$name}/parse", $this );
632
633 return $this->get_stylesheet()->__toString();
634 }
635
636 /**
637 * Add control style rules.
638 *
639 * Register new style rules for the control.
640 *
641 * @since 1.6.0
642 * @access private
643 *
644 * @param array $control The control.
645 * @param array $values Values array.
646 * @param array $controls The controls stack.
647 * @param array $placeholders Placeholders.
648 * @param array $replacements Replacements.
649 */
650 protected function add_control_style_rules( array $control, array $values, array $controls, array $placeholders, array $replacements ) {
651 $this->add_control_rules(
652 $control, $controls, function( $control ) use ( $values ) {
653 return $this->get_style_control_value( $control, $values );
654 }, $placeholders, $replacements, $values
655 );
656 }
657
658 /**
659 * Get style control value.
660 *
661 * Retrieve the value of the style control for any give control and values.
662 *
663 * It will retrieve the control name and return the style value.
664 *
665 * @since 1.6.0
666 * @access private
667 *
668 * @param array $control The control.
669 * @param array $values Values array.
670 *
671 * @return mixed Style control value.
672 */
673 private function get_style_control_value( array $control, array $values ) {
674 if ( ! empty( $values['__globals__'][ $control['name'] ] ) ) {
675 // When the control itself has no global value, but it refers to another control global value
676 return $this->get_selector_global_value( $control, $values['__globals__'][ $control['name'] ] );
677 }
678
679 $value = $values[ $control['name'] ];
680
681 if ( isset( $control['selectors_dictionary'][ $value ] ) ) {
682 $value = $control['selectors_dictionary'][ $value ];
683 }
684
685 if ( ! is_numeric( $value ) && ! is_float( $value ) && empty( $value ) ) {
686 return null;
687 }
688
689 return $value;
690 }
691
692 /**
693 * Init stylesheet.
694 *
695 * Initialize CSS file stylesheet by creating a new `Stylesheet` object and register new
696 * breakpoints for the stylesheet.
697 *
698 * @since 1.2.0
699 * @access private
700 */
701 private function init_stylesheet() {
702 $this->stylesheet_obj = new Stylesheet();
703
704 $active_breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints();
705
706 foreach ( $active_breakpoints as $breakpoint_name => $breakpoint ) {
707 $this->stylesheet_obj->add_device( $breakpoint_name, $breakpoint->get_value() );
708 }
709 }
710
711 /**
712 * Add repeater control style rules.
713 *
714 * Register new style rules for the repeater control.
715 *
716 * @since 2.0.0
717 * @access private
718 *
719 * @param Controls_Stack $controls_stack The control stack.
720 * @param array $repeater_control The repeater control.
721 * @param array $repeater_values Repeater values array.
722 * @param array $placeholders Placeholders.
723 * @param array $replacements Replacements.
724 */
725 protected function add_repeater_control_style_rules( Controls_Stack $controls_stack, array $repeater_control, array $repeater_values, array $placeholders, array $replacements ) {
726 $placeholders = array_merge( $placeholders, [ '{{CURRENT_ITEM}}' ] );
727
728 foreach ( $repeater_control['style_fields'] as $index => $item ) {
729 $this->add_controls_stack_style_rules(
730 $controls_stack,
731 $item,
732 $repeater_values[ $index ],
733 $placeholders,
734 array_merge( $replacements, [ '.elementor-repeater-item-' . $repeater_values[ $index ]['_id'] ] ),
735 $repeater_control['fields']
736 );
737 }
738 }
739
740 /**
741 * Add dynamic control style rules.
742 *
743 * Register new style rules for the dynamic control.
744 *
745 * @since 2.0.0
746 * @access private
747 *
748 * @param array $control The control.
749 * @param string $value The value.
750 */
751 protected function add_dynamic_control_style_rules( array $control, $value ) {
752 Plugin::$instance->dynamic_tags->parse_tags_text( $value, $control, function( $id, $name, $settings ) {
753 $tag = Plugin::$instance->dynamic_tags->create_tag( $id, $name, $settings );
754
755 if ( ! $tag instanceof Tag ) {
756 return;
757 }
758
759 $this->add_controls_stack_style_rules( $tag, $this->get_style_controls( $tag ), $tag->get_active_settings(), [ '{{WRAPPER}}' ], [ '#elementor-tag-' . $id ] );
760 } );
761 }
762
763 private function get_selector_global_value( $control, $global_key ) {
764 $data = Plugin::$instance->data_manager->run( $global_key );
765
766 if ( empty( $data['value'] ) ) {
767 return null;
768 }
769
770 $global_args = explode( '?id=', $global_key );
771
772 $id = $global_args[1];
773
774 if ( ! empty( $control['groupType'] ) ) {
775 $property_name = str_replace( [ $control['groupPrefix'], '_tablet', '_mobile' ], '', $control['name'] );
776
777 // TODO: This check won't retrieve the proper answer for array values (multiple controls).
778 if ( empty( $data['value'][ Global_Typography::TYPOGRAPHY_GROUP_PREFIX . $property_name ] ) ) {
779 return null;
780 }
781
782 $property_name = str_replace( '_', '-', $property_name );
783
784 $value = "var( --e-global-$control[groupType]-$id-$property_name )";
785
786 if ( $control['groupPrefix'] . 'font_family' === $control['name'] ) {
787 $default_generic_fonts = Plugin::$instance->kits_manager->get_current_settings( 'default_generic_fonts' );
788
789 if ( $default_generic_fonts ) {
790 $value .= ", $default_generic_fonts";
791 }
792 }
793 } else {
794 $value = "var( --e-global-$control[type]-$id )";
795 }
796
797 return $value;
798 }
799
800 final protected function get_active_controls( Controls_Stack $controls_stack, array $controls = null, array $settings = null ) {
801 if ( ! $controls ) {
802 $controls = $controls_stack->get_controls();
803 }
804
805 if ( ! $settings ) {
806 $settings = $controls_stack->get_controls_settings();
807 }
808
809 if ( $this->is_global_parsing_supported() ) {
810 $settings = $this->parse_global_settings( $settings, $controls );
811 }
812
813 $active_controls = array_reduce(
814 array_keys( $controls ), function( $active_controls, $control_key ) use ( $controls_stack, $controls, $settings ) {
815 $control = $controls[ $control_key ];
816
817 if ( $controls_stack->is_control_visible( $control, $settings ) ) {
818 $active_controls[ $control_key ] = $control;
819 }
820
821 return $active_controls;
822 }, []
823 );
824
825 return $active_controls;
826 }
827
828 final public function get_style_controls( Controls_Stack $controls_stack, array $controls = null, array $settings = null ) {
829 $controls = $this->get_active_controls( $controls_stack, $controls, $settings );
830
831 $style_controls = [];
832
833 foreach ( $controls as $control_name => $control ) {
834 $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] );
835
836 if ( ! $control_obj instanceof Base_Data_Control ) {
837 continue;
838 }
839
840 $control = array_merge( $control_obj->get_settings(), $control );
841
842 if ( $control_obj instanceof Control_Repeater ) {
843 $style_fields = [];
844
845 foreach ( $controls_stack->get_settings( $control_name ) as $item ) {
846 $style_fields[] = $this->get_style_controls( $controls_stack, $control['fields'], $item );
847 }
848
849 $control['style_fields'] = $style_fields;
850 }
851
852 if ( ! empty( $control['selectors'] ) || ! empty( $control['dynamic'] ) || $this->is_global_control( $controls_stack, $control_name, $controls ) || ! empty( $control['style_fields'] ) ) {
853 $style_controls[ $control_name ] = $control;
854 }
855 }
856
857 return $style_controls;
858 }
859
860 private function parse_global_settings( array $settings, array $controls ) {
861 foreach ( $controls as $control ) {
862 $control_name = $control['name'];
863 $control_obj = Plugin::$instance->controls_manager->get_control( $control['type'] );
864
865 if ( ! $control_obj instanceof Base_Data_Control ) {
866 continue;
867 }
868
869 if ( $control_obj instanceof Control_Repeater ) {
870 foreach ( $settings[ $control_name ] as & $field ) {
871 $field = $this->parse_global_settings( $field, $control['fields'] );
872 }
873
874 continue;
875 }
876
877 if ( empty( $control['global']['active'] ) ) {
878 continue;
879 }
880
881 if ( empty( $settings['__globals__'][ $control_name ] ) ) {
882 continue;
883 }
884
885 $settings[ $control_name ] = 'global';
886 }
887
888 return $settings;
889 }
890
891 private function is_global_control( Controls_Stack $controls_stack, $control_name, $controls ) {
892 $control = $controls[ $control_name ];
893
894 $control_global_key = $control_name;
895
896 if ( ! empty( $control['groupType'] ) ) {
897 $control_global_key = $control['groupPrefix'] . $control['groupType'];
898 }
899
900 if ( empty( $controls[ $control_global_key ]['global']['active'] ) ) {
901 return false;
902 }
903
904 $globals = $controls_stack->get_settings( '__globals__' );
905
906 return ! empty( $globals[ $control_global_key ] );
907 }
908 }
909