widget-base.php (28608B)
1 <?php 2 namespace Elementor; 3 4 use Elementor\Core\Page_Assets\Data_Managers\Widgets_Css as Widgets_Css_Data_Manager; 5 6 if ( ! defined( 'ABSPATH' ) ) { 7 exit; // Exit if accessed directly. 8 } 9 10 /** 11 * Elementor widget base. 12 * 13 * An abstract class to register new Elementor widgets. It extended the 14 * `Element_Base` class to inherit its properties. 15 * 16 * This abstract class must be extended in order to register new widgets. 17 * 18 * @since 1.0.0 19 * @abstract 20 */ 21 abstract class Widget_Base extends Element_Base { 22 /** 23 * Whether the widget has content. 24 * 25 * Used in cases where the widget has no content. When widgets uses only 26 * skins to display dynamic content generated on the server. For example the 27 * posts widget in Elementor Pro. Default is true, the widget has content 28 * template. 29 * 30 * @access protected 31 * 32 * @var bool 33 */ 34 protected $_has_template_content = true; 35 36 /** 37 * Registered Runtime Widgets. 38 * 39 * Registering in runtime all widgets that are being used on the page. 40 * 41 * @since 3.3.0 42 * @access public 43 * @static 44 * 45 * @var array 46 */ 47 public static $registered_runtime_widgets = []; 48 49 public static $registered_inline_css_widgets = []; 50 51 private static $widgets_css_data_manager; 52 53 /** 54 * Get element type. 55 * 56 * Retrieve the element type, in this case `widget`. 57 * 58 * @since 1.0.0 59 * @access public 60 * @static 61 * 62 * @return string The type. 63 */ 64 public static function get_type() { 65 return 'widget'; 66 } 67 68 /** 69 * Get widget icon. 70 * 71 * Retrieve the widget icon. 72 * 73 * @since 1.0.0 74 * @access public 75 * 76 * @return string Widget icon. 77 */ 78 public function get_icon() { 79 return 'eicon-apps'; 80 } 81 82 /** 83 * Get widget keywords. 84 * 85 * Retrieve the widget keywords. 86 * 87 * @since 1.0.10 88 * @access public 89 * 90 * @return array Widget keywords. 91 */ 92 public function get_keywords() { 93 return []; 94 } 95 96 /** 97 * Get widget categories. 98 * 99 * Retrieve the widget categories. 100 * 101 * @since 1.0.10 102 * @access public 103 * 104 * @return array Widget categories. 105 */ 106 public function get_categories() { 107 return [ 'general' ]; 108 } 109 110 /** 111 * Widget base constructor. 112 * 113 * Initializing the widget base class. 114 * 115 * @since 1.0.0 116 * @access public 117 * 118 * @throws \Exception If arguments are missing when initializing a full widget 119 * instance. 120 * 121 * @param array $data Widget data. Default is an empty array. 122 * @param array|null $args Optional. Widget default arguments. Default is null. 123 */ 124 public function __construct( $data = [], $args = null ) { 125 parent::__construct( $data, $args ); 126 127 $is_type_instance = $this->is_type_instance(); 128 129 if ( ! $is_type_instance && null === $args ) { 130 throw new \Exception( '`$args` argument is required when initializing a full widget instance.' ); 131 } 132 133 if ( $is_type_instance ) { 134 if ( $this->has_own_method( '_register_skins', self::class ) ) { 135 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( '_register_skins', '3.1.0', __CLASS__ . '::register_skins()' ); 136 137 $this->_register_skins(); 138 } else { 139 $this->register_skins(); 140 } 141 142 $widget_name = $this->get_name(); 143 144 /** 145 * Widget skin init. 146 * 147 * Fires when Elementor widget is being initialized. 148 * 149 * The dynamic portion of the hook name, `$widget_name`, refers to the widget name. 150 * 151 * @since 1.0.0 152 * 153 * @param Widget_Base $this The current widget. 154 */ 155 do_action( "elementor/widget/{$widget_name}/skins_init", $this ); 156 } 157 } 158 159 /** 160 * Get stack. 161 * 162 * Retrieve the widget stack of controls. 163 * 164 * @since 1.9.2 165 * @access public 166 * 167 * @param bool $with_common_controls Optional. Whether to include the common controls. Default is true. 168 * 169 * @return array Widget stack of controls. 170 */ 171 public function get_stack( $with_common_controls = true ) { 172 $stack = parent::get_stack(); 173 174 if ( $with_common_controls && 'common' !== $this->get_unique_name() ) { 175 /** @var Widget_Common $common_widget */ 176 $common_widget = Plugin::$instance->widgets_manager->get_widget_types( 'common' ); 177 178 $stack['controls'] = array_merge( $stack['controls'], $common_widget->get_controls() ); 179 180 $stack['tabs'] = array_merge( $stack['tabs'], $common_widget->get_tabs_controls() ); 181 } 182 183 return $stack; 184 } 185 186 /** 187 * Get widget controls pointer index. 188 * 189 * Retrieve widget pointer index where the next control should be added. 190 * 191 * While using injection point, it will return the injection point index. Otherwise index of the last control of the 192 * current widget itself without the common controls, plus one. 193 * 194 * @since 1.9.2 195 * @access public 196 * 197 * @return int Widget controls pointer index. 198 */ 199 public function get_pointer_index() { 200 $injection_point = $this->get_injection_point(); 201 202 if ( null !== $injection_point ) { 203 return $injection_point['index']; 204 } 205 206 return count( $this->get_stack( false )['controls'] ); 207 } 208 209 /** 210 * Show in panel. 211 * 212 * Whether to show the widget in the panel or not. By default returns true. 213 * 214 * @since 1.0.0 215 * @access public 216 * 217 * @return bool Whether to show the widget in the panel or not. 218 */ 219 public function show_in_panel() { 220 return true; 221 } 222 223 /** 224 * Start widget controls section. 225 * 226 * Used to add a new section of controls to the widget. Regular controls and 227 * skin controls. 228 * 229 * Note that when you add new controls to widgets they must be wrapped by 230 * `start_controls_section()` and `end_controls_section()`. 231 * 232 * @since 1.0.0 233 * @access public 234 * 235 * @param string $section_id Section ID. 236 * @param array $args Section arguments Optional. 237 */ 238 public function start_controls_section( $section_id, array $args = [] ) { 239 parent::start_controls_section( $section_id, $args ); 240 241 static $is_first_section = true; 242 243 if ( $is_first_section ) { 244 $this->register_skin_control(); 245 246 $is_first_section = false; 247 } 248 } 249 250 /** 251 * Register the Skin Control if the widget has skins. 252 * 253 * An internal method that is used to add a skin control to the widget. 254 * Added at the top of the controls section. 255 * 256 * @since 2.0.0 257 * @access private 258 */ 259 private function register_skin_control() { 260 $skins = $this->get_skins(); 261 if ( ! empty( $skins ) ) { 262 $skin_options = []; 263 264 if ( $this->_has_template_content ) { 265 $skin_options[''] = esc_html__( 'Default', 'elementor' ); 266 } 267 268 foreach ( $skins as $skin_id => $skin ) { 269 $skin_options[ $skin_id ] = $skin->get_title(); 270 } 271 272 // Get the first item for default value 273 $default_value = array_keys( $skin_options ); 274 $default_value = array_shift( $default_value ); 275 276 if ( 1 >= count( $skin_options ) ) { 277 $this->add_control( 278 '_skin', 279 [ 280 'label' => esc_html__( 'Skin', 'elementor' ), 281 'type' => Controls_Manager::HIDDEN, 282 'default' => $default_value, 283 ] 284 ); 285 } else { 286 $this->add_control( 287 '_skin', 288 [ 289 'label' => esc_html__( 'Skin', 'elementor' ), 290 'type' => Controls_Manager::SELECT, 291 'default' => $default_value, 292 'options' => $skin_options, 293 ] 294 ); 295 } 296 } 297 } 298 299 /** 300 * Register widget skins - deprecated prefixed method 301 * 302 * @since 1.7.12 303 * @access protected 304 * @deprecated 3.1.0 305 */ 306 protected function _register_skins() { 307 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0', __CLASS__ . '::register_skins()' ); 308 309 $this->register_skins(); 310 } 311 312 /** 313 * Register widget skins. 314 * 315 * This method is activated while initializing the widget base class. It is 316 * used to assign skins to widgets with `add_skin()` method. 317 * 318 * Usage: 319 * 320 * protected function register_skins() { 321 * $this->add_skin( new Skin_Classic( $this ) ); 322 * } 323 * 324 * @since 3.1.0 325 * @access protected 326 */ 327 protected function register_skins() {} 328 329 /** 330 * Get initial config. 331 * 332 * Retrieve the current widget initial configuration. 333 * 334 * Adds more configuration on top of the controls list, the tabs assigned to 335 * the control, element name, type, icon and more. This method also adds 336 * widget type, keywords and categories. 337 * 338 * @since 2.9.0 339 * @access protected 340 * 341 * @return array The initial widget config. 342 */ 343 protected function get_initial_config() { 344 $config = [ 345 'widget_type' => $this->get_name(), 346 'keywords' => $this->get_keywords(), 347 'categories' => $this->get_categories(), 348 'html_wrapper_class' => $this->get_html_wrapper_class(), 349 'show_in_panel' => $this->show_in_panel(), 350 ]; 351 352 $stack = Plugin::$instance->controls_manager->get_element_stack( $this ); 353 354 if ( $stack ) { 355 $config['controls'] = $this->get_stack( false )['controls']; 356 $config['tabs_controls'] = $this->get_tabs_controls(); 357 } 358 359 return array_merge( parent::get_initial_config(), $config ); 360 } 361 362 /** 363 * @since 2.3.1 364 * @access protected 365 */ 366 protected function should_print_empty() { 367 return false; 368 } 369 370 /** 371 * Print widget content template. 372 * 373 * Used to generate the widget content template on the editor, using a 374 * Backbone JavaScript template. 375 * 376 * @since 2.0.0 377 * @access protected 378 * 379 * @param string $template_content Template content. 380 */ 381 protected function print_template_content( $template_content ) { 382 ?> 383 <div class="elementor-widget-container"> 384 <?php 385 echo $template_content; // XSS ok. 386 ?> 387 </div> 388 <?php 389 } 390 391 /** 392 * Parse text editor. 393 * 394 * Parses the content from rich text editor with shortcodes, oEmbed and 395 * filtered data. 396 * 397 * @since 1.0.0 398 * @access protected 399 * 400 * @param string $content Text editor content. 401 * 402 * @return string Parsed content. 403 */ 404 protected function parse_text_editor( $content ) { 405 /** This filter is documented in wp-includes/widgets/class-wp-widget-text.php */ 406 $content = apply_filters( 'widget_text', $content, $this->get_settings() ); 407 408 $content = shortcode_unautop( $content ); 409 $content = do_shortcode( $content ); 410 $content = wptexturize( $content ); 411 412 if ( $GLOBALS['wp_embed'] instanceof \WP_Embed ) { 413 $content = $GLOBALS['wp_embed']->autoembed( $content ); 414 } 415 416 return $content; 417 } 418 419 /** 420 * Safe print parsed text editor. 421 * 422 * @uses static::parse_text_editor. 423 * 424 * @access protected 425 * 426 * @param string $content Text editor content. 427 */ 428 final protected function print_text_editor( $content ) { 429 // PHPCS - the method `parse_text_editor` is safe. 430 echo static::parse_text_editor( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 431 } 432 433 /** 434 * Get HTML wrapper class. 435 * 436 * Retrieve the widget container class. Can be used to override the 437 * container class for specific widgets. 438 * 439 * @since 2.0.9 440 * @access protected 441 */ 442 protected function get_html_wrapper_class() { 443 return 'elementor-widget-' . $this->get_name(); 444 } 445 446 /** 447 * Add widget render attributes. 448 * 449 * Used to add attributes to the current widget wrapper HTML tag. 450 * 451 * @since 1.0.0 452 * @access protected 453 */ 454 protected function add_render_attributes() { 455 parent::add_render_attributes(); 456 457 $this->add_render_attribute( 458 '_wrapper', 'class', [ 459 'elementor-widget', 460 $this->get_html_wrapper_class(), 461 ] 462 ); 463 464 $settings = $this->get_settings(); 465 466 $this->add_render_attribute( '_wrapper', 'data-widget_type', $this->get_name() . '.' . ( ! empty( $settings['_skin'] ) ? $settings['_skin'] : 'default' ) ); 467 } 468 469 /** 470 * Add lightbox data to image link. 471 * 472 * Used to add lightbox data attributes to image link HTML. 473 * 474 * @since 2.9.1 475 * @access public 476 * 477 * @param string $link_html Image link HTML. 478 * @param string $id Attachment id. 479 * 480 * @return string Image link HTML with lightbox data attributes. 481 */ 482 public function add_lightbox_data_to_image_link( $link_html, $id ) { 483 $settings = $this->get_settings_for_display(); 484 $open_lightbox = isset( $settings['open_lightbox'] ) ? $settings['open_lightbox'] : null; 485 486 if ( Plugin::$instance->editor->is_edit_mode() ) { 487 $this->add_render_attribute( 'link', 'class', 'elementor-clickable', true ); 488 } 489 490 $this->add_lightbox_data_attributes( 'link', $id, $open_lightbox, $this->get_id(), true ); 491 return preg_replace( '/^<a/', '<a ' . $this->get_render_attribute_string( 'link' ), $link_html ); 492 } 493 494 /** 495 * Add Light-Box attributes. 496 * 497 * Used to add Light-Box-related data attributes to links that open media files. 498 * 499 * @param array|string $element The link HTML element. 500 * @param int $id The ID of the image 501 * @param string $lightbox_setting_key The setting key that dictates weather to open the image in a lightbox 502 * @param string $group_id Unique ID for a group of lightbox images 503 * @param bool $overwrite Optional. Whether to overwrite existing 504 * attribute. Default is false, not to overwrite. 505 * 506 * @return Widget_Base Current instance of the widget. 507 * @since 2.9.0 508 * @access public 509 * 510 */ 511 public function add_lightbox_data_attributes( $element, $id = null, $lightbox_setting_key = null, $group_id = null, $overwrite = false ) { 512 $kit = Plugin::$instance->kits_manager->get_active_kit(); 513 514 $is_global_image_lightbox_enabled = 'yes' === $kit->get_settings( 'global_image_lightbox' ); 515 516 if ( 'no' === $lightbox_setting_key ) { 517 if ( $is_global_image_lightbox_enabled ) { 518 $this->add_render_attribute( $element, 'data-elementor-open-lightbox', 'no', $overwrite ); 519 } 520 521 return $this; 522 } 523 524 if ( 'yes' !== $lightbox_setting_key && ! $is_global_image_lightbox_enabled ) { 525 return $this; 526 } 527 528 $attributes['data-elementor-open-lightbox'] = 'yes'; 529 530 if ( $group_id ) { 531 $attributes['data-elementor-lightbox-slideshow'] = $group_id; 532 } 533 534 if ( $id ) { 535 $lightbox_image_attributes = Plugin::$instance->images_manager->get_lightbox_image_attributes( $id ); 536 537 if ( isset( $lightbox_image_attributes['title'] ) ) { 538 $attributes['data-elementor-lightbox-title'] = $lightbox_image_attributes['title']; 539 } 540 541 if ( isset( $lightbox_image_attributes['description'] ) ) { 542 $attributes['data-elementor-lightbox-description'] = $lightbox_image_attributes['description']; 543 } 544 } 545 546 $this->add_render_attribute( $element, $attributes, null, $overwrite ); 547 548 return $this; 549 } 550 551 /** 552 * Render widget output on the frontend. 553 * 554 * Used to generate the final HTML displayed on the frontend. 555 * 556 * Note that if skin is selected, it will be rendered by the skin itself, 557 * not the widget. 558 * 559 * @since 1.0.0 560 * @access public 561 */ 562 public function render_content() { 563 /** 564 * Before widget render content. 565 * 566 * Fires before Elementor widget is being rendered. 567 * 568 * @since 1.0.0 569 * 570 * @param Widget_Base $this The current widget. 571 */ 572 do_action( 'elementor/widget/before_render_content', $this ); 573 574 ob_start(); 575 576 $skin = $this->get_current_skin(); 577 if ( $skin ) { 578 $skin->set_parent( $this ); 579 $skin->render_by_mode(); 580 } else { 581 $this->render_by_mode(); 582 } 583 584 $widget_content = ob_get_clean(); 585 586 if ( empty( $widget_content ) ) { 587 return; 588 } 589 ?> 590 <div class="elementor-widget-container"> 591 <?php 592 if ( $this->is_widget_first_render( $this->get_group_name() ) ) { 593 $this->register_runtime_widget( $this->get_group_name() ); 594 } 595 596 $this->print_widget_css(); 597 598 // get_name 599 600 /** 601 * Render widget content. 602 * 603 * Filters the widget content before it's rendered. 604 * 605 * @since 1.0.0 606 * 607 * @param string $widget_content The content of the widget. 608 * @param Widget_Base $this The widget. 609 */ 610 $widget_content = apply_filters( 'elementor/widget/render_content', $widget_content, $this ); 611 612 echo $widget_content; // XSS ok. 613 ?> 614 </div> 615 <?php 616 } 617 618 protected function is_widget_first_render( $widget_name ) { 619 return ! in_array( $widget_name, self::$registered_runtime_widgets, true ); 620 } 621 622 /** 623 * Render widget plain content. 624 * 625 * Elementor saves the page content in a unique way, but it's not the way 626 * WordPress saves data. This method is used to save generated HTML to the 627 * database as plain content the WordPress way. 628 * 629 * When rendering plain content, it allows other WordPress plugins to 630 * interact with the content - to search, check SEO and other purposes. It 631 * also allows the site to keep working even if Elementor is deactivated. 632 * 633 * Note that if the widget uses shortcodes to display the data, the best 634 * practice is to return the shortcode itself. 635 * 636 * Also note that if the widget don't display any content it should return 637 * an empty string. For example Elementor Pro Form Widget uses this method 638 * to return an empty string because there is no content to return. This way 639 * if Elementor Pro will be deactivated there won't be any form to display. 640 * 641 * @since 1.0.0 642 * @access public 643 */ 644 public function render_plain_content() { 645 $this->render_content(); 646 } 647 648 /** 649 * Before widget rendering. 650 * 651 * Used to add stuff before the widget `_wrapper` element. 652 * 653 * @since 1.0.0 654 * @access public 655 */ 656 public function before_render() { 657 ?> 658 <div <?php $this->print_render_attribute_string( '_wrapper' ); ?>> 659 <?php 660 } 661 662 /** 663 * After widget rendering. 664 * 665 * Used to add stuff after the widget `_wrapper` element. 666 * 667 * @since 1.0.0 668 * @access public 669 */ 670 public function after_render() { 671 ?> 672 </div> 673 <?php 674 } 675 676 /** 677 * Get the element raw data. 678 * 679 * Retrieve the raw element data, including the id, type, settings, child 680 * elements and whether it is an inner element. 681 * 682 * The data with the HTML used always to display the data, but the Elementor 683 * editor uses the raw data without the HTML in order not to render the data 684 * again. 685 * 686 * @since 1.0.0 687 * @access public 688 * 689 * @param bool $with_html_content Optional. Whether to return the data with 690 * HTML content or without. Used for caching. 691 * Default is false, without HTML. 692 * 693 * @return array Element raw data. 694 */ 695 public function get_raw_data( $with_html_content = false ) { 696 $data = parent::get_raw_data( $with_html_content ); 697 698 unset( $data['isInner'] ); 699 700 $data['widgetType'] = $this->get_data( 'widgetType' ); 701 702 if ( $with_html_content ) { 703 ob_start(); 704 705 $this->render_content(); 706 707 $data['htmlCache'] = ob_get_clean(); 708 } 709 710 return $data; 711 } 712 713 /** 714 * Print widget content. 715 * 716 * Output the widget final HTML on the frontend. 717 * 718 * @since 1.0.0 719 * @access protected 720 */ 721 protected function print_content() { 722 $this->render_content(); 723 } 724 725 /** 726 * Print a setting content without escaping. 727 * 728 * Script tags are allowed on frontend according to the WP theme securing policy. 729 * 730 * @param string $setting 731 * @param null $repeater_name 732 * @param null $index 733 */ 734 final protected function print_unescaped_setting( $setting, $repeater_name = null, $index = null ) { 735 if ( $repeater_name ) { 736 $repeater = $this->get_settings_for_display( $repeater_name ); 737 $output = $repeater[ $index ][ $setting ]; 738 } else { 739 $output = $this->get_settings_for_display( $setting ); 740 } 741 742 echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 743 } 744 745 /** 746 * Get default data. 747 * 748 * Retrieve the default widget data. Used to reset the data on initialization. 749 * 750 * @since 1.0.0 751 * @access protected 752 * 753 * @return array Default data. 754 */ 755 protected function get_default_data() { 756 $data = parent::get_default_data(); 757 758 $data['widgetType'] = ''; 759 760 return $data; 761 } 762 763 /** 764 * Get default child type. 765 * 766 * Retrieve the widget child type based on element data. 767 * 768 * @since 1.0.0 769 * @access protected 770 * 771 * @param array $element_data Widget ID. 772 * 773 * @return array|false Child type or false if it's not a valid widget. 774 */ 775 protected function _get_default_child_type( array $element_data ) { 776 return Plugin::$instance->elements_manager->get_element_types( 'section' ); 777 } 778 779 /** 780 * Get repeater setting key. 781 * 782 * Retrieve the unique setting key for the current repeater item. Used to connect the current element in the 783 * repeater to it's settings model and it's control in the panel. 784 * 785 * PHP usage (inside `Widget_Base::render()` method): 786 * 787 * $tabs = $this->get_settings( 'tabs' ); 788 * foreach ( $tabs as $index => $item ) { 789 * $tab_title_setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $index ); 790 * $this->add_inline_editing_attributes( $tab_title_setting_key, 'none' ); 791 * echo '<div ' . $this->get_render_attribute_string( $tab_title_setting_key ) . '>' . $item['tab_title'] . '</div>'; 792 * } 793 * 794 * @since 1.8.0 795 * @access protected 796 * 797 * @param string $setting_key The current setting key inside the repeater item (e.g. `tab_title`). 798 * @param string $repeater_key The repeater key containing the array of all the items in the repeater (e.g. `tabs`). 799 * @param int $repeater_item_index The current item index in the repeater array (e.g. `3`). 800 * 801 * @return string The repeater setting key (e.g. `tabs.3.tab_title`). 802 */ 803 protected function get_repeater_setting_key( $setting_key, $repeater_key, $repeater_item_index ) { 804 return implode( '.', [ $repeater_key, $repeater_item_index, $setting_key ] ); 805 } 806 807 /** 808 * Add inline editing attributes. 809 * 810 * Define specific area in the element to be editable inline. The element can have several areas, with this method 811 * you can set the area inside the element that can be edited inline. You can also define the type of toolbar the 812 * user will see, whether it will be a basic toolbar or an advanced one. 813 * 814 * Note: When you use wysiwyg control use the advanced toolbar, with textarea control use the basic toolbar. Text 815 * control should not have toolbar. 816 * 817 * PHP usage (inside `Widget_Base::render()` method): 818 * 819 * $this->add_inline_editing_attributes( 'text', 'advanced' ); 820 * echo '<div ' . $this->get_render_attribute_string( 'text' ) . '>' . $this->get_settings( 'text' ) . '</div>'; 821 * 822 * @since 1.8.0 823 * @access protected 824 * 825 * @param string $key Element key. 826 * @param string $toolbar Optional. Toolbar type. Accepted values are `advanced`, `basic` or `none`. Default is 827 * `basic`. 828 */ 829 protected function add_inline_editing_attributes( $key, $toolbar = 'basic' ) { 830 if ( ! Plugin::$instance->editor->is_edit_mode() ) { 831 return; 832 } 833 834 $this->add_render_attribute( $key, [ 835 'class' => 'elementor-inline-editing', 836 'data-elementor-setting-key' => $key, 837 ] ); 838 839 if ( 'basic' !== $toolbar ) { 840 $this->add_render_attribute( $key, [ 841 'data-elementor-inline-editing-toolbar' => $toolbar, 842 ] ); 843 } 844 } 845 846 /** 847 * Add new skin. 848 * 849 * Register new widget skin to allow the user to set custom designs. Must be 850 * called inside the `register_skins()` method. 851 * 852 * @since 1.0.0 853 * @access public 854 * 855 * @param Skin_Base $skin Skin instance. 856 */ 857 public function add_skin( Skin_Base $skin ) { 858 Plugin::$instance->skins_manager->add_skin( $this, $skin ); 859 } 860 861 /** 862 * Get single skin. 863 * 864 * Retrieve a single skin based on skin ID, from all the skin assigned to 865 * the widget. If the skin does not exist or not assigned to the widget, 866 * return false. 867 * 868 * @since 1.0.0 869 * @access public 870 * 871 * @param string $skin_id Skin ID. 872 * 873 * @return string|false Single skin, or false. 874 */ 875 public function get_skin( $skin_id ) { 876 $skins = $this->get_skins(); 877 if ( isset( $skins[ $skin_id ] ) ) { 878 return $skins[ $skin_id ]; 879 } 880 881 return false; 882 } 883 884 /** 885 * Get current skin ID. 886 * 887 * Retrieve the ID of the current skin. 888 * 889 * @since 1.0.0 890 * @access public 891 * 892 * @return string Current skin. 893 */ 894 public function get_current_skin_id() { 895 return $this->get_settings( '_skin' ); 896 } 897 898 /** 899 * Get current skin. 900 * 901 * Retrieve the current skin, or if non exist return false. 902 * 903 * @since 1.0.0 904 * @access public 905 * 906 * @return Skin_Base|false Current skin or false. 907 */ 908 public function get_current_skin() { 909 return $this->get_skin( $this->get_current_skin_id() ); 910 } 911 912 /** 913 * Remove widget skin. 914 * 915 * Unregister an existing skin and remove it from the widget. 916 * 917 * @since 1.0.0 918 * @access public 919 * 920 * @param string $skin_id Skin ID. 921 * 922 * @return \WP_Error|true Whether the skin was removed successfully from the widget. 923 */ 924 public function remove_skin( $skin_id ) { 925 return Plugin::$instance->skins_manager->remove_skin( $this, $skin_id ); 926 } 927 928 /** 929 * Get widget skins. 930 * 931 * Retrieve all the skin assigned to the widget. 932 * 933 * @since 1.0.0 934 * @access public 935 * 936 * @return Skin_Base[] 937 */ 938 public function get_skins() { 939 return Plugin::$instance->skins_manager->get_skins( $this ); 940 } 941 942 /** 943 * Get group name. 944 * 945 * Some widgets need to use group names, this method allows you to create them. 946 * By default it retrieves the regular name. 947 * 948 * @since 3.3.0 949 * @access public 950 * 951 * @return string Unique name. 952 */ 953 public function get_group_name() { 954 return $this->get_name(); 955 } 956 957 /** 958 * Get Inline CSS dependencies. 959 * 960 * Retrieve a list of inline CSS dependencies that the element requires. 961 * 962 * @since 3.3.0 963 * @access public 964 * 965 * @return array. 966 */ 967 public function get_inline_css_depends() { 968 return []; 969 } 970 971 /** 972 * @param string $plugin_title Plugin's title 973 * @param string $since Plugin version widget was deprecated 974 * @param string $last Plugin version in which the widget will be removed 975 * @param string $replacement Widget replacement 976 */ 977 protected function deprecated_notice( $plugin_title, $since, $last = '', $replacement = '' ) { 978 $this->start_controls_section( 979 'Deprecated', 980 [ 981 'label' => esc_html__( 'Deprecated', 'elementor' ), 982 ] 983 ); 984 985 $this->add_control( 986 'deprecated_notice', 987 [ 988 'type' => Controls_Manager::DEPRECATED_NOTICE, 989 'widget' => $this->get_title(), 990 'since' => $since, 991 'last' => $last, 992 'plugin' => $plugin_title, 993 'replacement' => $replacement, 994 ] 995 ); 996 997 $this->end_controls_section(); 998 999 } 1000 1001 public function register_runtime_widget( $widget_name ) { 1002 self::$registered_runtime_widgets[] = $widget_name; 1003 } 1004 1005 public function get_widget_css_config( $widget_name ) { 1006 $direction = is_rtl() ? '-rtl' : ''; 1007 1008 $css_file_path = 'css/widget-' . $widget_name . $direction . '.min.css'; 1009 1010 return [ 1011 'key' => $widget_name, 1012 'version' => ELEMENTOR_VERSION, 1013 'file_path' => ELEMENTOR_ASSETS_PATH . $css_file_path, 1014 'data' => [ 1015 'file_url' => ELEMENTOR_ASSETS_URL . $css_file_path, 1016 ], 1017 ]; 1018 } 1019 1020 public function get_css_config() { 1021 return $this->get_widget_css_config( $this->get_group_name() ); 1022 } 1023 1024 private function get_widget_css() { 1025 $widgets_css_data_manager = $this->get_widgets_css_data_manager(); 1026 1027 $widgets_list = $this->get_inline_css_depends(); 1028 1029 $widgets_list[] = $this->get_group_name(); 1030 1031 $widget_css = ''; 1032 1033 foreach ( $widgets_list as $widget_data ) { 1034 $widget_name = isset( $widget_data['name'] ) ? $widget_data['name'] : $widget_data; 1035 1036 if ( ! in_array( $widget_name, self::$registered_inline_css_widgets, true ) ) { 1037 if ( $this->get_group_name() === $widget_name ) { 1038 $config = $this->get_css_config(); 1039 } else { 1040 /** 1041 * The core-dependency allowing to create a dependency specifically with the core widgets. 1042 * Otherwise, the config will be taken from the class that inherits from Widget_Base. 1043 */ 1044 $is_core_dependency = isset( $widget_data['is_core_dependency'] ) ? true : false; 1045 1046 $config = $is_core_dependency ? self::get_widget_css_config( $widget_name ) : $this->get_widget_css_config( $widget_name ); 1047 } 1048 1049 $widget_css .= $widgets_css_data_manager->get_asset_data( $config ); 1050 1051 self::$registered_inline_css_widgets[] = $widget_name; 1052 } 1053 } 1054 1055 return $widget_css; 1056 1057 } 1058 1059 private function is_inline_css_mode() { 1060 static $is_active; 1061 1062 if ( null === $is_active ) { 1063 $is_edit_mode = Plugin::$instance->editor->is_edit_mode(); 1064 $is_preview_mode = Plugin::$instance->preview->is_preview_mode(); 1065 $is_optimized_mode = Plugin::$instance->experiments->is_feature_active( 'e_optimized_css_loading' ); 1066 1067 $is_active = ( Utils::is_script_debug() || $is_edit_mode || $is_preview_mode || ! $is_optimized_mode ) ? false : true; 1068 } 1069 1070 return $is_active; 1071 } 1072 1073 private function print_widget_css() { 1074 if ( ! $this->is_inline_css_mode() ) { 1075 return; 1076 } 1077 1078 Utils::print_unescaped_internal_string( $this->get_widget_css() ); 1079 } 1080 1081 private function get_widgets_css_data_manager() { 1082 if ( ! self::$widgets_css_data_manager ) { 1083 self::$widgets_css_data_manager = new Widgets_Css_Data_Manager(); 1084 } 1085 1086 return self::$widgets_css_data_manager; 1087 } 1088 }