PluginProbe
Elementor Website Builder – more than just a page builder / 3.0.0
Elementor Website Builder – more than just a page builder v3.0.0
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.0.0, at core/files/css/base.php

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