PluginProbe
Elementor Website Builder – more than just a page builder / 4.1.5
Elementor Website Builder – more than just a page builder v4.1.5
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 / managers / widgets.php

widgets.php in Elementor Website Builder – more than just a page builder 4.1.5, at includes/managers/widgets.php

791 lines 18.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 use Elementor\Core\Common\Modules\Ajax\Module as Ajax;
5 use Elementor\Core\Utils\Collection;
6 use Elementor\Core\Utils\Force_Locale;
7 use Elementor\Modules\AtomicWidgets\Elements\Atomic_Button\Atomic_Button;
8 use Elementor\Modules\AtomicWidgets\Elements\Atomic_Divider\Atomic_Divider;
9 use Elementor\Modules\AtomicWidgets\Elements\Atomic_Heading\Atomic_Heading;
10 use Elementor\Modules\AtomicWidgets\Elements\Atomic_Image\Atomic_Image;
11 use Elementor\Modules\AtomicWidgets\Elements\Atomic_Paragraph\Atomic_Paragraph;
12 use Elementor\Modules\AtomicWidgets\Elements\Atomic_Svg\Atomic_Svg;
13 use Elementor\Modules\NestedAccordion\Widgets\Nested_Accordion;
14 use Elementor\Modules\NestedElements\Module as NestedElementsModule;
15 use Elementor\Modules\NestedTabs\Widgets\NestedTabs;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit; // Exit if accessed directly.
19 }
20
21 /**
22 * Elementor widgets manager.
23 *
24 * Elementor widgets manager handler class is responsible for registering and
25 * initializing all the supported Elementor widgets.
26 *
27 * @since 1.0.0
28 */
29 class Widgets_Manager {
30
31 /**
32 * Widget types.
33 *
34 * Holds the list of all the widget types.
35 *
36 * @since 1.0.0
37 * @access private
38 *
39 * @var Widget_Base[]
40 */
41 private $_widget_types = null;
42
43 /**
44 * Promoted widget types.
45 *
46 * Holds the list of all the Promoted widget types.
47 *
48 * @since 3.15.0
49 * @access private
50 *
51 * @var Widget_Base[]
52 *
53 * @return array
54 */
55 private array $promoted_widgets = [
56 NestedElementsModule::EXPERIMENT_NAME => [
57 NestedTabs::class,
58 Nested_Accordion::class,
59 ],
60 'atomic_widgets' => [
61 Atomic_Heading::class,
62 Atomic_Image::class,
63 Atomic_Paragraph::class,
64 Atomic_Button::class,
65 Atomic_Svg::class,
66 Atomic_Divider::class,
67 ],
68 ];
69
70 /**
71 * Init widgets.
72 *
73 * Initialize Elementor widgets manager. Include all the widgets files
74 * and register each Elementor and WordPress widget.
75 *
76 * @since 2.0.0
77 * @access private
78 */
79 private function init_widgets() {
80 $build_widgets_filename = [
81 'common-base',
82 'common',
83 'common-optimized',
84 'inner-section',
85 'heading',
86 'image',
87 'text-editor',
88 'video',
89 'button',
90 'divider',
91 'spacer',
92 'image-box',
93 'google-maps',
94 'icon',
95 'icon-box',
96 'star-rating',
97 'image-carousel',
98 'image-gallery',
99 'icon-list',
100 'counter',
101 'progress',
102 'testimonial',
103 'tabs',
104 'accordion',
105 'toggle',
106 'social-icons',
107 'alert',
108 'audio',
109 'shortcode',
110 'html',
111 'menu-anchor',
112 'sidebar',
113 'read-more',
114 'rating',
115 ];
116
117 $this->_widget_types = [];
118
119 $this->register_promoted_widgets();
120
121 foreach ( $build_widgets_filename as $widget_filename ) {
122 include ELEMENTOR_PATH . 'includes/widgets/' . $widget_filename . '.php';
123
124 $class_name = str_replace( '-', '_', $widget_filename );
125
126 $class_name = __NAMESPACE__ . '\Widget_' . $class_name;
127
128 $this->register( new $class_name() );
129 }
130
131 $this->register_wp_widgets();
132
133 /**
134 * After widgets registered.
135 *
136 * Fires after Elementor widgets are registered.
137 *
138 * @since 1.0.0
139 * @deprecated 3.5.0 Use `elementor/widgets/register` hook instead.
140 *
141 * @param Widgets_Manager $this The widgets manager.
142 */
143 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->do_deprecated_action(
144 'elementor/widgets/widgets_registered',
145 [ $this ],
146 '3.5.0',
147 'elementor/widgets/register'
148 );
149
150 /**
151 * After widgets registered.
152 *
153 * Fires after Elementor widgets are registered.
154 *
155 * @since 3.5.0
156 *
157 * @param Widgets_Manager $this The widgets manager.
158 */
159 do_action( 'elementor/widgets/register', $this );
160 }
161
162 /**
163 * Register WordPress widgets.
164 *
165 * Add native WordPress widget to the list of registered widget types.
166 *
167 * Exclude the widgets that are in Elementor widgets black list. Theme and
168 * plugin authors can filter the black list.
169 *
170 * @since 2.0.0
171 * @access private
172 */
173 private function register_wp_widgets() {
174 global $wp_widget_factory;
175
176 // Allow themes/plugins to filter out their widgets.
177 $black_list = [];
178
179 /**
180 * Elementor widgets black list.
181 *
182 * Filters the widgets black list that won't be displayed in the panel.
183 *
184 * @since 1.0.0
185 *
186 * @param array $black_list A black list of widgets. Default is an empty array.
187 */
188 $black_list = apply_filters( 'elementor/widgets/black_list', $black_list );
189
190 foreach ( $wp_widget_factory->widgets as $widget_class => $widget_obj ) {
191
192 if ( in_array( $widget_class, $black_list ) ) {
193 continue;
194 }
195
196 $elementor_widget_class = __NAMESPACE__ . '\Widget_WordPress';
197
198 $this->register(
199 new $elementor_widget_class( [], [
200 'widget_name' => $widget_class,
201 ] )
202 );
203 }
204 }
205
206 /**
207 * Require files.
208 *
209 * Require Elementor widget base class.
210 *
211 * @since 2.0.0
212 * @access private
213 */
214 private function require_files() {
215 require ELEMENTOR_PATH . 'includes/base/widget-base.php';
216 }
217
218 private function pluck_default_controls( $controls ) {
219 return ( new Collection( $controls ) )
220 ->reduce( function ( $controls_defaults, $control, $control_key ) {
221 if ( ! empty( $control['default'] ) ) {
222 $controls_defaults[ $control_key ]['default'] = $control['default'];
223 }
224
225 return $controls_defaults;
226 }, [] );
227 }
228
229 /**
230 * Register widget type.
231 *
232 * Add a new widget type to the list of registered widget types.
233 *
234 * @since 1.0.0
235 * @access public
236 * @deprecated 3.5.0 Use `register()` method instead.
237 *
238 * @param Widget_Base $widget Elementor widget.
239 *
240 * @return true True if the widget was registered.
241 */
242 public function register_widget_type( Widget_Base $widget ) {
243 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
244 __METHOD__,
245 '3.5.0',
246 'register()'
247 );
248
249 return $this->register( $widget );
250 }
251
252 /**
253 * Register a new widget type.
254 *
255 * @param \Elementor\Widget_Base $widget_instance Elementor Widget.
256 *
257 * @return bool True if the widget was registered.
258 * @since 3.5.0
259 * @access public
260 */
261 public function register( Widget_Base $widget_instance ) {
262 if ( is_null( $this->_widget_types ) ) {
263 $this->init_widgets();
264 }
265
266 /**
267 * Should widget be registered.
268 *
269 * @since 3.18.0
270 *
271 * @param bool $should_register Should widget be registered. Default is `true`.
272 * @param \Elementor\Widget_Base $widget_instance Widget instance.
273 */
274 $should_register = apply_filters( 'elementor/widgets/is_widget_enabled', true, $widget_instance );
275
276 if ( ! $should_register ) {
277 return false;
278 }
279
280 $this->_widget_types[ $widget_instance->get_name() ] = $widget_instance;
281
282 return true;
283 }
284
285 /** Register promoted widgets
286 *
287 * Since we cannot allow widgets to place themselves is a specific
288 * location on our widgets panel we need to use a hard coded solution for this.
289 *
290 * @return void
291 */
292 private function register_promoted_widgets() {
293
294 foreach ( $this->promoted_widgets as $experiment_name => $classes ) {
295 $this->register_promoted_active_widgets( $experiment_name, $classes );
296 }
297 }
298
299 /**
300 * Unregister widget type.
301 *
302 * Removes widget type from the list of registered widget types.
303 *
304 * @since 1.0.0
305 * @access public
306 * @deprecated 3.5.0 Use `unregister()` method instead.
307 *
308 * @param string $name Widget name.
309 *
310 * @return true True if the widget was unregistered, False otherwise.
311 */
312 public function unregister_widget_type( $name ) {
313 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
314 __METHOD__,
315 '3.5.0',
316 'unregister()'
317 );
318
319 return $this->unregister( $name );
320 }
321
322 /**
323 * Unregister widget type.
324 *
325 * Removes widget type from the list of registered widget types.
326 *
327 * @since 3.5.0
328 * @access public
329 *
330 * @param string $name Widget name.
331 *
332 * @return boolean Whether the widget was unregistered.
333 */
334 public function unregister( $name ) {
335 if ( ! isset( $this->_widget_types[ $name ] ) ) {
336 return false;
337 }
338
339 unset( $this->_widget_types[ $name ] );
340
341 return true;
342 }
343
344 /**
345 * Get widget types.
346 *
347 * Retrieve the registered widget types list.
348 *
349 * @since 1.0.0
350 * @access public
351 *
352 * @param string $widget_name Optional. Widget name. Default is null.
353 *
354 * @return Widget_Base|Widget_Base[]|null Registered widget types.
355 */
356 public function get_widget_types( $widget_name = null ) {
357 if ( is_null( $this->_widget_types ) ) {
358 $this->init_widgets();
359 }
360
361 if ( null !== $widget_name ) {
362 return isset( $this->_widget_types[ $widget_name ] ) ? $this->_widget_types[ $widget_name ] : null;
363 }
364
365 return $this->_widget_types;
366 }
367
368 /**
369 * Get widget types config.
370 *
371 * Retrieve all the registered widgets with config for each widgets.
372 *
373 * @since 1.0.0
374 * @access public
375 *
376 * @return array Registered widget types with each widget config.
377 */
378 public function get_widget_types_config() {
379 $config = [];
380
381 foreach ( $this->get_widget_types() as $widget_key => $widget ) {
382 $config[ $widget_key ] = $widget->get_config();
383 }
384
385 return $config;
386 }
387
388 /**
389 * @throws \Exception If current user don't have permissions to edit the post.
390 */
391 public function ajax_get_widget_types_controls_config( array $data ) {
392 Plugin::$instance->documents->check_permissions( $data['editor_post_id'] );
393
394 wp_raise_memory_limit( 'admin' );
395
396 $config = [];
397
398 foreach ( $this->get_widget_types() as $widget_key => $widget ) {
399 if ( isset( $data['exclude'][ $widget_key ] ) ) {
400 continue;
401 }
402
403 $config[ $widget_key ] = [
404 'controls' => $widget->get_stack( false )['controls'],
405 'tabs_controls' => $widget->get_tabs_controls(),
406 ];
407 }
408
409 return $config;
410 }
411
412 /**
413 * @throws \Exception If current user don't have permissions to edit the post.
414 */
415 public function ajax_refresh_widgets_config( array $data ) {
416 Plugin::$instance->documents->check_permissions( $data['editor_post_id'] );
417
418 wp_raise_memory_limit( 'admin' );
419
420 $widgets = [];
421
422 foreach ( $this->get_widget_types() as $widget_key => $widget ) {
423 $widget_config = $widget->get_config();
424
425 // get_config() omits controls when the stack isn't initialized yet (see Widget_Base::get_initial_config).
426 // During an AJAX refresh the instances are fresh, so we force-initialize and merge them explicitly.
427 $widget_config['controls'] = $widget->get_stack( false )['controls'];
428 $widget_config['tabs_controls'] = $widget->get_tabs_controls();
429
430 $widgets[ $widget_key ] = $widget_config;
431 }
432
433 return [
434 'widgets' => $widgets,
435 'categories' => Plugin::$instance->elements_manager->get_categories(),
436 ];
437 }
438
439 public function ajax_get_widgets_default_value_translations( array $data = [] ) {
440 $locale = empty( $data['locale'] )
441 ? get_locale()
442 : $data['locale'];
443
444 $force_locale = new Force_Locale( $locale );
445 $force_locale->force();
446
447 $controls = ( new Collection( $this->get_widget_types() ) )
448 ->map( function ( Widget_Base $widget ) {
449 $controls = $widget->get_stack( false )['controls'];
450
451 return [
452 'controls' => $this->pluck_default_controls( $controls ),
453 ];
454 } )
455 ->filter( function ( $widget ) {
456 return ! empty( $widget['controls'] );
457 } )
458 ->all();
459
460 $force_locale->restore();
461
462 return $controls;
463 }
464
465 /**
466 * Ajax render widget.
467 *
468 * Ajax handler for Elementor render_widget.
469 *
470 * Fired by `wp_ajax_elementor_render_widget` action.
471 *
472 * @since 1.0.0
473 * @access public
474 *
475 * @throws \Exception If current user don't have permissions to edit the post.
476 *
477 * @param array $request Ajax request.
478 *
479 * @return array {
480 * Rendered widget.
481 *
482 * @type string $render The rendered HTML.
483 * }
484 */
485 public function ajax_render_widget( $request ) {
486 $document = Plugin::$instance->documents->get_with_permissions( $request['editor_post_id'] );
487
488 // Override the global $post for the render.
489 query_posts(
490 [
491 'p' => $request['editor_post_id'],
492 'post_type' => 'any',
493 ]
494 );
495
496 $editor = Plugin::$instance->editor;
497 $is_edit_mode = $editor->is_edit_mode();
498 $editor->set_edit_mode( true );
499
500 Plugin::$instance->documents->switch_to_document( $document );
501
502 $render_html = $document->render_element( $request['data'] );
503
504 $editor->set_edit_mode( $is_edit_mode );
505
506 return [
507 'render' => $render_html,
508 ];
509 }
510
511 /**
512 * Ajax get WordPress widget form.
513 *
514 * Ajax handler for Elementor editor get_wp_widget_form.
515 *
516 * Fired by `wp_ajax_elementor_editor_get_wp_widget_form` action.
517 *
518 * @since 1.0.0
519 * @access public
520 *
521 * @param array $request Ajax request.
522 *
523 * @return bool|string Rendered widget form.
524 * @throws \Exception If current user don't have permissions to edit the post.
525 */
526 public function ajax_get_wp_widget_form( $request ) {
527 Plugin::$instance->documents->check_permissions( $request['editor_post_id'] );
528
529 if ( empty( $request['widget_type'] ) ) {
530 return false;
531 }
532
533 if ( empty( $request['data'] ) ) {
534 $request['data'] = [];
535 }
536
537 $element_data = [
538 'id' => $request['id'],
539 'elType' => 'widget',
540 'widgetType' => $request['widget_type'],
541 'settings' => $request['data'],
542 ];
543
544 /**
545 * @var $widget_obj Widget_WordPress
546 */
547 $widget_obj = Plugin::$instance->elements_manager->create_element_instance( $element_data );
548
549 if ( ! $widget_obj ) {
550 return false;
551 }
552
553 return $widget_obj->get_form();
554 }
555
556 /**
557 * Render widgets content.
558 *
559 * Used to generate the widget templates on the editor using Underscore JS
560 * template, for all the registered widget types.
561 *
562 * @since 1.0.0
563 * @access public
564 */
565 public function render_widgets_content() {
566 foreach ( $this->get_widget_types() as $widget ) {
567 $widget->print_template();
568 }
569 }
570
571 /**
572 * Get widgets frontend settings keys.
573 *
574 * Retrieve frontend controls settings keys for all the registered widget
575 * types.
576 *
577 * @since 1.3.0
578 * @access public
579 *
580 * @return array Registered widget types with settings keys for each widget.
581 */
582 public function get_widgets_frontend_settings_keys() {
583 $keys = [];
584
585 foreach ( $this->get_widget_types() as $widget_type_name => $widget_type ) {
586 $widget_type_keys = $widget_type->get_frontend_settings_keys();
587
588 if ( $widget_type_keys ) {
589 $keys[ $widget_type_name ] = $widget_type_keys;
590 }
591 }
592
593 return $keys;
594 }
595
596 /**
597 * Widgets with styles.
598 *
599 * This method returns the list of all the widgets in the `/includes/`
600 * folder that have styles.
601 *
602 * @since 3.24.0
603 * @access public
604 *
605 * @return array The names of the widgets that have styles.
606 */
607 public function widgets_with_styles(): array {
608 return [
609 'counter',
610 'divider',
611 'google_maps',
612 'heading',
613 'image',
614 'image-carousel',
615 'menu-anchor',
616 'rating',
617 'social-icons',
618 'spacer',
619 'testimonial',
620 'text-editor',
621 'video',
622 ];
623 }
624
625 /**
626 * Widgets with responsive styles.
627 *
628 * This method returns the list of all the widgets in the `/includes/`
629 * folder that have responsive styles.
630 *
631 * @since 3.24.0
632 * @access public
633 *
634 * @return array The names of the widgets that have responsive styles.
635 */
636 public function widgets_with_responsive_styles(): array {
637 return [
638 'accordion',
639 'alert',
640 'icon-box',
641 'icon-list',
642 'image-box',
643 'image-gallery',
644 'progress',
645 'star-rating',
646 'tabs',
647 'toggle',
648 ];
649 }
650
651 /**
652 * Enqueue widgets scripts.
653 *
654 * Enqueue all the scripts defined as a dependency for each widget.
655 *
656 * @since 1.3.0
657 * @access public
658 */
659 public function enqueue_widgets_scripts() {
660 foreach ( $this->get_widget_types() as $widget ) {
661 $widget->enqueue_scripts();
662 }
663 }
664
665 public function register_frontend_handlers() {
666 foreach ( $this->get_widget_types() as $widget ) {
667 $widget->register_frontend_handlers();
668 }
669 }
670
671 /**
672 * Enqueue widgets styles
673 *
674 * Enqueue all the styles defined as a dependency for each widget
675 *
676 * @access public
677 */
678 public function enqueue_widgets_styles() {
679 foreach ( $this->get_widget_types() as $widget ) {
680 $widget->enqueue_styles();
681 }
682 }
683
684 /**
685 * Retrieve inline editing configuration.
686 *
687 * Returns general inline editing configurations like toolbar types etc.
688 *
689 * @access public
690 * @since 1.8.0
691 *
692 * @return array {
693 * Inline editing configuration.
694 *
695 * @type array $toolbar {
696 * Toolbar types and the actions each toolbar includes.
697 * Note: Wysiwyg controls uses the advanced toolbar, textarea controls
698 * uses the basic toolbar and text controls has no toolbar.
699 *
700 * @type array $basic Basic actions included in the edit tool.
701 * @type array $advanced Advanced actions included in the edit tool.
702 * }
703 * }
704 */
705 public function get_inline_editing_config() {
706 $basic_tools = [
707 'bold',
708 'underline',
709 'italic',
710 ];
711
712 $advanced_tools = array_merge( $basic_tools, [
713 'createlink',
714 'unlink',
715 'h1' => [
716 'h1',
717 'h2',
718 'h3',
719 'h4',
720 'h5',
721 'h6',
722 'p',
723 'blockquote',
724 'pre',
725 ],
726 'list' => [
727 'insertOrderedList',
728 'insertUnorderedList',
729 ],
730 ] );
731
732 return [
733 'toolbar' => [
734 'basic' => $basic_tools,
735 'advanced' => $advanced_tools,
736 ],
737 ];
738 }
739
740 /**
741 * Widgets manager constructor.
742 *
743 * Initializing Elementor widgets manager.
744 *
745 * @since 1.0.0
746 * @access public
747 */
748 public function __construct() {
749 $this->require_files();
750
751 add_action( 'elementor/ajax/register_actions', [ $this, 'register_ajax_actions' ] );
752 }
753
754 /**
755 * Register ajax actions.
756 *
757 * Add new actions to handle data after an ajax requests returned.
758 *
759 * @since 2.0.0
760 * @access public
761 *
762 * @param Ajax $ajax_manager
763 */
764 public function register_ajax_actions( Ajax $ajax_manager ) {
765 $ajax_manager->register_ajax_action( 'render_widget', [ $this, 'ajax_render_widget' ] );
766 $ajax_manager->register_ajax_action( 'editor_get_wp_widget_form', [ $this, 'ajax_get_wp_widget_form' ] );
767 $ajax_manager->register_ajax_action( 'get_widgets_config', [ $this, 'ajax_get_widget_types_controls_config' ] );
768
769 $ajax_manager->register_ajax_action( 'refresh_widgets_config', [ $this, 'ajax_refresh_widgets_config' ] );
770
771 $ajax_manager->register_ajax_action( 'get_widgets_default_value_translations', function ( array $data ) {
772 return $this->ajax_get_widgets_default_value_translations( $data );
773 } );
774 }
775
776 /**
777 * @param string $experiment_name
778 * @param array $classes
779 * @return void
780 */
781 public function register_promoted_active_widgets( string $experiment_name, array $classes ): void {
782 if ( ! Plugin::$instance->experiments->is_feature_active( $experiment_name ) || empty( $classes ) ) {
783 return;
784 }
785
786 foreach ( $classes as $class_name ) {
787 $this->register( new $class_name() );
788 }
789 }
790 }
791