PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0-beta2
Elementor Website Builder – more than just a page builder v4.3.0-beta2
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.3.0-beta2, at includes/managers/widgets.php

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