PluginProbe
Elementor Website Builder – more than just a page builder / 3.4.0-dev9
Elementor Website Builder – more than just a page builder v3.4.0-dev9
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 / includes / base / widget-base.php

widget-base.php in Elementor Website Builder – more than just a page builder 3.4.0-dev9, at includes/base/widget-base.php

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