PluginProbe
Elementor Website Builder – more than just a page builder / 3.4.2
Elementor Website Builder – more than just a page builder v3.4.2
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 / element-base.php

element-base.php in Elementor Website Builder – more than just a page builder 3.4.2, at includes/base/element-base.php

869 lines 20.3 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 if ( ! defined( 'ABSPATH' ) ) {
5 exit; // Exit if accessed directly.
6 }
7
8 /**
9 * Elementor element base.
10 *
11 * An abstract class to register new Elementor elements. It extended the
12 * `Controls_Stack` class to inherit its properties.
13 *
14 * This abstract class must be extended in order to register new elements.
15 *
16 * @since 1.0.0
17 * @abstract
18 */
19 abstract class Element_Base extends Controls_Stack {
20
21 /**
22 * Child elements.
23 *
24 * Holds all the child elements of the element.
25 *
26 * @access private
27 *
28 * @var Element_Base[]
29 */
30 private $children;
31
32 /**
33 * Element default arguments.
34 *
35 * Holds all the default arguments of the element. Used to store additional
36 * data. For example WordPress widgets use this to store widget names.
37 *
38 * @access private
39 *
40 * @var array
41 */
42 private $default_args = [];
43
44 /**
45 * Is type instance.
46 *
47 * Whether the element is an instance of that type or not.
48 *
49 * @access private
50 *
51 * @var bool
52 */
53 private $is_type_instance = true;
54
55 /**
56 * Depended scripts.
57 *
58 * Holds all the element depended scripts to enqueue.
59 *
60 * @since 1.9.0
61 * @access private
62 *
63 * @var array
64 */
65 private $depended_scripts = [];
66
67 /**
68 * Depended styles.
69 *
70 * Holds all the element depended styles to enqueue.
71 *
72 * @since 1.9.0
73 * @access private
74 *
75 * @var array
76 */
77 private $depended_styles = [];
78
79 /**
80 * Add script depends.
81 *
82 * Register new script to enqueue by the handler.
83 *
84 * @since 1.9.0
85 * @access public
86 *
87 * @param string $handler Depend script handler.
88 */
89 public function add_script_depends( $handler ) {
90 $this->depended_scripts[] = $handler;
91 }
92
93 /**
94 * Add style depends.
95 *
96 * Register new style to enqueue by the handler.
97 *
98 * @since 1.9.0
99 * @access public
100 *
101 * @param string $handler Depend style handler.
102 */
103 public function add_style_depends( $handler ) {
104 $this->depended_styles[] = $handler;
105 }
106
107 /**
108 * Get script dependencies.
109 *
110 * Retrieve the list of script dependencies the element requires.
111 *
112 * @since 1.3.0
113 * @access public
114 *
115 * @return array Element scripts dependencies.
116 */
117 public function get_script_depends() {
118 return $this->depended_scripts;
119 }
120
121 /**
122 * Enqueue scripts.
123 *
124 * Registers all the scripts defined as element dependencies and enqueues
125 * them. Use `get_script_depends()` method to add custom script dependencies.
126 *
127 * @since 1.3.0
128 * @access public
129 */
130 final public function enqueue_scripts() {
131 $deprecated_scripts = [
132 'jquery-slick' => [
133 'version' => '2.7.0',
134 'replacement' => 'Swiper',
135 ],
136 ];
137
138 foreach ( $this->get_script_depends() as $script ) {
139 if ( isset( $deprecated_scripts[ $script ] ) ) {
140 Utils::handle_deprecation( $script, $deprecated_scripts[ $script ]['version'], $deprecated_scripts[ $script ]['replacement'] );
141 }
142
143 wp_enqueue_script( $script );
144 }
145 }
146
147 /**
148 * Get style dependencies.
149 *
150 * Retrieve the list of style dependencies the element requires.
151 *
152 * @since 1.9.0
153 * @access public
154 *
155 * @return array Element styles dependencies.
156 */
157 public function get_style_depends() {
158 return $this->depended_styles;
159 }
160
161 /**
162 * Enqueue styles.
163 *
164 * Registers all the styles defined as element dependencies and enqueues
165 * them. Use `get_style_depends()` method to add custom style dependencies.
166 *
167 * @since 1.9.0
168 * @access public
169 */
170 final public function enqueue_styles() {
171 foreach ( $this->get_style_depends() as $style ) {
172 wp_enqueue_style( $style );
173 }
174 }
175
176 /**
177 * @since 1.0.0
178 * @deprecated 2.6.0
179 * @access public
180 * @static
181 */
182 final public static function add_edit_tool() {}
183
184 /**
185 * @since 2.2.0
186 * @deprecated 2.6.0
187 * @access public
188 * @static
189 */
190 final public static function is_edit_buttons_enabled() {
191 return get_option( 'elementor_edit_buttons' );
192 }
193
194 /**
195 * Get default child type.
196 *
197 * Retrieve the default child type based on element data.
198 *
199 * Note that not all elements support children.
200 *
201 * @since 1.0.0
202 * @access protected
203 * @abstract
204 *
205 * @param array $element_data Element data.
206 *
207 * @return Element_Base
208 */
209 abstract protected function _get_default_child_type( array $element_data );
210
211 /**
212 * Before element rendering.
213 *
214 * Used to add stuff before the element.
215 *
216 * @since 1.0.0
217 * @access public
218 */
219 public function before_render() {}
220
221 /**
222 * After element rendering.
223 *
224 * Used to add stuff after the element.
225 *
226 * @since 1.0.0
227 * @access public
228 */
229 public function after_render() {}
230
231 /**
232 * Get element title.
233 *
234 * Retrieve the element title.
235 *
236 * @since 1.0.0
237 * @access public
238 *
239 * @return string Element title.
240 */
241 public function get_title() {
242 return '';
243 }
244
245 /**
246 * Get element icon.
247 *
248 * Retrieve the element icon.
249 *
250 * @since 1.0.0
251 * @access public
252 *
253 * @return string Element icon.
254 */
255 public function get_icon() {
256 return 'eicon-columns';
257 }
258
259 public function get_help_url() {
260 return 'https://go.elementor.com/widget-' . $this->get_name();
261 }
262
263 public function get_custom_help_url() {
264 return '';
265 }
266
267 /**
268 * Whether the reload preview is required.
269 *
270 * Used to determine whether the reload preview is required or not.
271 *
272 * @since 1.0.0
273 * @access public
274 *
275 * @return bool Whether the reload preview is required.
276 */
277 public function is_reload_preview_required() {
278 return false;
279 }
280
281 /**
282 * @since 2.3.1
283 * @access protected
284 */
285 protected function should_print_empty() {
286 return true;
287 }
288
289 /**
290 * Get child elements.
291 *
292 * Retrieve all the child elements of this element.
293 *
294 * @since 1.0.0
295 * @access public
296 *
297 * @return Element_Base[] Child elements.
298 */
299 public function get_children() {
300 if ( null === $this->children ) {
301 $this->init_children();
302 }
303
304 return $this->children;
305 }
306
307 /**
308 * Get default arguments.
309 *
310 * Retrieve the element default arguments. Used to return all the default
311 * arguments or a specific default argument, if one is set.
312 *
313 * @since 1.0.0
314 * @access public
315 *
316 * @param array $item Optional. Default is null.
317 *
318 * @return array Default argument(s).
319 */
320 public function get_default_args( $item = null ) {
321 return self::get_items( $this->default_args, $item );
322 }
323
324 /**
325 * Add new child element.
326 *
327 * Register new child element to allow hierarchy.
328 *
329 * @since 1.0.0
330 * @access public
331 * @param array $child_data Child element data.
332 * @param array $child_args Child element arguments.
333 *
334 * @return Element_Base|false Child element instance, or false if failed.
335 */
336 public function add_child( array $child_data, array $child_args = [] ) {
337 if ( null === $this->children ) {
338 $this->init_children();
339 }
340
341 $child_type = $this->get_child_type( $child_data );
342
343 if ( ! $child_type ) {
344 return false;
345 }
346
347 $child = Plugin::$instance->elements_manager->create_element_instance( $child_data, $child_args, $child_type );
348
349 if ( $child ) {
350 $this->children[] = $child;
351 }
352
353 return $child;
354 }
355
356 /**
357 * Add link render attributes.
358 *
359 * Used to add link tag attributes to a specific HTML element.
360 *
361 * The HTML link tag is represented by the element parameter. The `url_control` parameter
362 * needs to be an array of link settings in the same format they are set by Elementor's URL control.
363 *
364 * Example usage:
365 *
366 * `$this->add_link_attributes( 'button', $settings['link'] );`
367 *
368 * @since 2.8.0
369 * @access public
370 *
371 * @param array|string $element The HTML element.
372 * @param array $url_control Array of link settings.
373 * @param bool $overwrite Optional. Whether to overwrite existing
374 * attribute. Default is false, not to overwrite.
375 *
376 * @return Element_Base Current instance of the element.
377 */
378
379 public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
380 $attributes = [];
381
382 if ( ! empty( $url_control['url'] ) ) {
383 $allowed_protocols = array_merge( wp_allowed_protocols(), [ 'skype', 'viber' ] );
384
385 $attributes['href'] = esc_url( $url_control['url'], $allowed_protocols );
386 }
387
388 if ( ! empty( $url_control['is_external'] ) ) {
389 $attributes['target'] = '_blank';
390 }
391
392 if ( ! empty( $url_control['nofollow'] ) ) {
393 $attributes['rel'] = 'nofollow';
394 }
395
396 if ( ! empty( $url_control['custom_attributes'] ) ) {
397 // Custom URL attributes should come as a string of comma-delimited key|value pairs
398 $attributes = array_merge( $attributes, Utils::parse_custom_attributes( $url_control['custom_attributes'] ) );
399 }
400
401 if ( $attributes ) {
402 $this->add_render_attribute( $element, $attributes, $overwrite );
403 }
404
405 return $this;
406 }
407
408 /**
409 * Print element.
410 *
411 * Used to generate the element final HTML on the frontend and the editor.
412 *
413 * @since 1.0.0
414 * @access public
415 */
416 public function print_element() {
417 $element_type = $this->get_type();
418
419 /**
420 * Before frontend element render.
421 *
422 * Fires before Elementor element is rendered in the frontend.
423 *
424 * @since 2.2.0
425 *
426 * @param Element_Base $this The element.
427 */
428 do_action( 'elementor/frontend/before_render', $this );
429
430 /**
431 * Before frontend element render.
432 *
433 * Fires before Elementor element is rendered in the frontend.
434 *
435 * The dynamic portion of the hook name, `$element_type`, refers to the element type.
436 *
437 * @since 1.0.0
438 *
439 * @param Element_Base $this The element.
440 */
441 do_action( "elementor/frontend/{$element_type}/before_render", $this );
442
443 ob_start();
444
445 if ( $this->has_own_method( '_print_content', self::class ) ) {
446 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( '_print_content', '3.1.0', __CLASS__ . '::print_content()' );
447
448 $this->_print_content();
449 } else {
450 $this->print_content();
451 }
452
453 $content = ob_get_clean();
454
455 $should_render = ( ! empty( $content ) || $this->should_print_empty() );
456
457 /**
458 * Should the element be rendered for frontend
459 *
460 * Filters if the element should be rendered on frontend.
461 *
462 * @since 2.3.3
463 *
464 * @param bool true The element.
465 * @param Element_Base $this The element.
466 */
467 $should_render = apply_filters( "elementor/frontend/{$element_type}/should_render", $should_render, $this );
468
469 if ( $should_render ) {
470 if ( $this->has_own_method( '_add_render_attributes', self::class ) ) {
471 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( '_add_render_attributes', '3.1.0', __CLASS__ . '::add_render_attributes()' );
472
473 $this->_add_render_attributes();
474 } else {
475 $this->add_render_attributes();
476 }
477
478 $this->before_render();
479 // PHPCS - The content has already been escaped by the `render` method.
480 echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
481 $this->after_render();
482
483 $this->enqueue_scripts();
484 $this->enqueue_styles();
485 }
486
487 /**
488 * After frontend element render.
489 *
490 * Fires after Elementor element is rendered in the frontend.
491 *
492 * The dynamic portion of the hook name, `$element_type`, refers to the element type.
493 *
494 * @since 1.0.0
495 *
496 * @param Element_Base $this The element.
497 */
498 do_action( "elementor/frontend/{$element_type}/after_render", $this );
499
500 /**
501 * After frontend element render.
502 *
503 * Fires after Elementor element is rendered in the frontend.
504 *
505 * @since 2.3.0
506 *
507 * @param Element_Base $this The element.
508 */
509 do_action( 'elementor/frontend/after_render', $this );
510 }
511
512 /**
513 * Get the element raw data.
514 *
515 * Retrieve the raw element data, including the id, type, settings, child
516 * elements and whether it is an inner element.
517 *
518 * The data with the HTML used always to display the data, but the Elementor
519 * editor uses the raw data without the HTML in order not to render the data
520 * again.
521 *
522 * @since 1.0.0
523 * @access public
524 *
525 * @param bool $with_html_content Optional. Whether to return the data with
526 * HTML content or without. Used for caching.
527 * Default is false, without HTML.
528 *
529 * @return array Element raw data.
530 */
531 public function get_raw_data( $with_html_content = false ) {
532 $data = $this->get_data();
533
534 $elements = [];
535
536 foreach ( $this->get_children() as $child ) {
537 $elements[] = $child->get_raw_data( $with_html_content );
538 }
539
540 return [
541 'id' => $this->get_id(),
542 'elType' => $data['elType'],
543 'settings' => $data['settings'],
544 'elements' => $elements,
545 'isInner' => $data['isInner'],
546 ];
547 }
548
549 /**
550 * Get unique selector.
551 *
552 * Retrieve the unique selector of the element. Used to set a unique HTML
553 * class for each HTML element. This way Elementor can set custom styles for
554 * each element.
555 *
556 * @since 1.0.0
557 * @access public
558 *
559 * @return string Unique selector.
560 */
561 public function get_unique_selector() {
562 return '.elementor-element-' . $this->get_id();
563 }
564
565 /**
566 * Is type instance.
567 *
568 * Used to determine whether the element is an instance of that type or not.
569 *
570 * @since 1.0.0
571 * @access public
572 *
573 * @return bool Whether the element is an instance of that type.
574 */
575 public function is_type_instance() {
576 return $this->is_type_instance;
577 }
578
579 /**
580 * Add render attributes.
581 *
582 * Used to add attributes to the current element wrapper HTML tag.
583 *
584 * @since 1.3.0
585 * @access protected
586 * @deprecated 3.1.0
587 */
588 protected function _add_render_attributes() {
589 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0', __CLASS__ . '::add_render_attributes()' );
590
591 return $this->add_render_attributes();
592 }
593
594 /**
595 * Add render attributes.
596 *
597 * Used to add attributes to the current element wrapper HTML tag.
598 *
599 * @since 3.1.0
600 * @access protected
601 */
602 protected function add_render_attributes() {
603 $id = $this->get_id();
604
605 $settings = $this->get_settings_for_display();
606 $frontend_settings = $this->get_frontend_settings();
607 $controls = $this->get_controls();
608
609 $this->add_render_attribute( '_wrapper', [
610 'class' => [
611 'elementor-element',
612 'elementor-element-' . $id,
613 ],
614 'data-id' => $id,
615 'data-element_type' => $this->get_type(),
616 ] );
617
618 $class_settings = [];
619
620 foreach ( $settings as $setting_key => $setting ) {
621 if ( isset( $controls[ $setting_key ]['prefix_class'] ) ) {
622 $class_settings[ $setting_key ] = $setting;
623 }
624 }
625
626 foreach ( $class_settings as $setting_key => $setting ) {
627 if ( empty( $setting ) && '0' !== $setting ) {
628 continue;
629 }
630
631 $this->add_render_attribute( '_wrapper', 'class', $controls[ $setting_key ]['prefix_class'] . $setting );
632 }
633
634 $_animation = ! empty( $settings['_animation'] );
635 $animation = ! empty( $settings['animation'] );
636 $has_animation = $_animation && 'none' !== $settings['_animation'] || $animation && 'none' !== $settings['animation'];
637
638 if ( $has_animation ) {
639 $is_static_render_mode = Plugin::$instance->frontend->is_static_render_mode();
640
641 if ( ! $is_static_render_mode ) {
642 // Hide the element until the animation begins
643 $this->add_render_attribute( '_wrapper', 'class', 'elementor-invisible' );
644 }
645 }
646
647 if ( ! empty( $settings['_element_id'] ) ) {
648 $this->add_render_attribute( '_wrapper', 'id', trim( $settings['_element_id'] ) );
649 }
650
651 if ( $frontend_settings ) {
652 $this->add_render_attribute( '_wrapper', 'data-settings', wp_json_encode( $frontend_settings ) );
653 }
654
655 /**
656 * After element attribute rendered.
657 *
658 * Fires after the attributes of the element HTML tag are rendered.
659 *
660 * @since 2.3.0
661 *
662 * @param Element_Base $this The element.
663 */
664 do_action( 'elementor/element/after_add_attributes', $this );
665 }
666
667 /**
668 * Add Hidden Device Controls
669 *
670 * Adds controls for hiding elements within certain devices' viewport widths. Adds a control for each active device.
671 *
672 * @since 3.4.0
673 * @access protected
674 */
675 protected function add_hidden_device_controls() {
676 // The 'Hide On X' controls are displayed from largest to smallest, while the method returns smallest to largest.
677 $active_devices = Plugin::$instance->breakpoints->get_active_devices_list( [ 'reverse' => true ] );
678 $active_breakpoints = Plugin::$instance->breakpoints->get_active_breakpoints();
679
680 foreach ( $active_devices as $breakpoint_key ) {
681 $label = 'desktop' === $breakpoint_key ? __( 'Desktop', 'elementor' ) : $active_breakpoints[ $breakpoint_key ]->get_label();
682
683 $this->add_control(
684 'hide_' . $breakpoint_key,
685 [
686 /* translators: %s: Device Name. */
687 'label' => sprintf( __( 'Hide On %s', 'elementor' ), $label ),
688 'type' => Controls_Manager::SWITCHER,
689 'default' => '',
690 'prefix_class' => 'elementor-',
691 'label_on' => 'Hide',
692 'label_off' => 'Show',
693 'return_value' => 'hidden-' . $breakpoint_key,
694 ]
695 );
696 }
697 }
698
699 /**
700 * Get default data.
701 *
702 * Retrieve the default element data. Used to reset the data on initialization.
703 *
704 * @since 1.0.0
705 * @access protected
706 *
707 * @return array Default data.
708 */
709 protected function get_default_data() {
710 $data = parent::get_default_data();
711
712 return array_merge(
713 $data, [
714 'elements' => [],
715 'isInner' => false,
716 ]
717 );
718 }
719
720 /**
721 * Print element content.
722 *
723 * Output the element final HTML on the frontend.
724 *
725 * @since 1.0.0
726 * @access protected
727 */
728 protected function _print_content() {
729 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0', __CLASS__ . '::print_content()' );
730
731 $this->print_content();
732 }
733
734 /**
735 * Print element content.
736 *
737 * Output the element final HTML on the frontend.
738 *
739 * @since 3.1.0
740 * @access protected
741 */
742 protected function print_content() {
743 foreach ( $this->get_children() as $child ) {
744 $child->print_element();
745 }
746 }
747
748 /**
749 * Get initial config.
750 *
751 * Retrieve the current element initial configuration.
752 *
753 * Adds more configuration on top of the controls list and the tabs assigned
754 * to the control. This method also adds element name, type, icon and more.
755 *
756 * @since 2.9.0
757 * @access protected
758 *
759 * @return array The initial config.
760 */
761 protected function get_initial_config() {
762 $config = [
763 'name' => $this->get_name(),
764 'elType' => $this->get_type(),
765 'title' => $this->get_title(),
766 'icon' => $this->get_icon(),
767 'reload_preview' => $this->is_reload_preview_required(),
768 ];
769
770 if ( preg_match( '/^' . __NAMESPACE__ . '(Pro)?\\\\/', get_called_class() ) ) {
771 $config['help_url'] = $this->get_help_url();
772 } else {
773 $config['help_url'] = $this->get_custom_help_url();
774 }
775
776 if ( ! $this->is_editable() ) {
777 $config['editable'] = false;
778 }
779
780 return $config;
781 }
782
783 /**
784 * Get child type.
785 *
786 * Retrieve the element child type based on element data.
787 *
788 * @since 2.0.0
789 * @access private
790 *
791 * @param array $element_data Element ID.
792 *
793 * @return Element_Base|false Child type or false if type not found.
794 */
795 private function get_child_type( $element_data ) {
796 $child_type = $this->_get_default_child_type( $element_data );
797
798 // If it's not a valid widget ( like a deactivated plugin )
799 if ( ! $child_type ) {
800 return false;
801 }
802
803 /**
804 * Element child type.
805 *
806 * Filters the child type of the element.
807 *
808 * @since 1.0.0
809 *
810 * @param Element_Base $child_type The child element.
811 * @param array $element_data The original element ID.
812 * @param Element_Base $this The original element.
813 */
814 $child_type = apply_filters( 'elementor/element/get_child_type', $child_type, $element_data, $this );
815
816 return $child_type;
817 }
818
819 /**
820 * Initialize children.
821 *
822 * Initializing the element child elements.
823 *
824 * @since 2.0.0
825 * @access private
826 */
827 private function init_children() {
828 $this->children = [];
829
830 $children_data = $this->get_data( 'elements' );
831
832 if ( ! $children_data ) {
833 return;
834 }
835
836 foreach ( $children_data as $child_data ) {
837 if ( ! $child_data ) {
838 continue;
839 }
840
841 $this->add_child( $child_data );
842 }
843 }
844
845 /**
846 * Element base constructor.
847 *
848 * Initializing the element base class using `$data` and `$args`.
849 *
850 * The `$data` parameter is required for a normal instance because of the
851 * way Elementor renders data when initializing elements.
852 *
853 * @since 1.0.0
854 * @access public
855 *
856 * @param array $data Optional. Element data. Default is an empty array.
857 * @param array|null $args Optional. Element default arguments. Default is null.
858 **/
859 public function __construct( array $data = [], array $args = null ) {
860 if ( $data ) {
861 $this->is_type_instance = false;
862 } elseif ( $args ) {
863 $this->default_args = $args;
864 }
865
866 parent::__construct( $data );
867 }
868 }
869