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