PluginProbe
Elementor Website Builder – more than just a page builder / 3.13.0-beta1
Elementor Website Builder – more than just a page builder v3.13.0-beta1
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 / controls.php

controls.php in Elementor Website Builder – more than just a page builder 3.13.0-beta1, at includes/managers/controls.php

1,144 lines 25.5 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 controls manager.
10 *
11 * Elementor controls manager handler class is responsible for registering and
12 * initializing all the supported controls, both regular controls and the group
13 * controls.
14 *
15 * @since 1.0.0
16 */
17 class Controls_Manager {
18
19 /**
20 * Content tab.
21 */
22 const TAB_CONTENT = 'content';
23
24 /**
25 * Style tab.
26 */
27 const TAB_STYLE = 'style';
28
29 /**
30 * Advanced tab.
31 */
32 const TAB_ADVANCED = 'advanced';
33
34 /**
35 * Responsive tab.
36 */
37 const TAB_RESPONSIVE = 'responsive';
38
39 /**
40 * Layout tab.
41 */
42 const TAB_LAYOUT = 'layout';
43
44 /**
45 * Settings tab.
46 */
47 const TAB_SETTINGS = 'settings';
48
49 /**
50 * Text control.
51 */
52 const TEXT = 'text';
53
54 /**
55 * Number control.
56 */
57 const NUMBER = 'number';
58
59 /**
60 * Textarea control.
61 */
62 const TEXTAREA = 'textarea';
63
64 /**
65 * Select control.
66 */
67 const SELECT = 'select';
68
69 /**
70 * Switcher control.
71 */
72 const SWITCHER = 'switcher';
73
74 /**
75 * Button control.
76 */
77 const BUTTON = 'button';
78
79 /**
80 * Hidden control.
81 */
82 const HIDDEN = 'hidden';
83
84 /**
85 * Heading control.
86 */
87 const HEADING = 'heading';
88
89 /**
90 * Raw HTML control.
91 */
92 const RAW_HTML = 'raw_html';
93
94 /**
95 * Deprecated Notice control.
96 */
97 const DEPRECATED_NOTICE = 'deprecated_notice';
98
99 /**
100 * Popover Toggle control.
101 */
102 const POPOVER_TOGGLE = 'popover_toggle';
103
104 /**
105 * Section control.
106 */
107 const SECTION = 'section';
108
109 /**
110 * Tab control.
111 */
112 const TAB = 'tab';
113
114 /**
115 * Tabs control.
116 */
117 const TABS = 'tabs';
118
119 /**
120 * Divider control.
121 */
122 const DIVIDER = 'divider';
123
124 /**
125 * Color control.
126 */
127 const COLOR = 'color';
128
129 /**
130 * Media control.
131 */
132 const MEDIA = 'media';
133
134 /**
135 * Slider control.
136 */
137 const SLIDER = 'slider';
138
139 /**
140 * Dimensions control.
141 */
142 const DIMENSIONS = 'dimensions';
143
144 /**
145 * Choose control.
146 */
147 const CHOOSE = 'choose';
148
149 /**
150 * WYSIWYG control.
151 */
152 const WYSIWYG = 'wysiwyg';
153
154 /**
155 * Code control.
156 */
157 const CODE = 'code';
158
159 /**
160 * Font control.
161 */
162 const FONT = 'font';
163
164 /**
165 * Image dimensions control.
166 */
167 const IMAGE_DIMENSIONS = 'image_dimensions';
168
169 /**
170 * WordPress widget control.
171 */
172 const WP_WIDGET = 'wp_widget';
173
174 /**
175 * URL control.
176 */
177 const URL = 'url';
178
179 /**
180 * Repeater control.
181 */
182 const REPEATER = 'repeater';
183
184 /**
185 * Icon control.
186 */
187 const ICON = 'icon';
188
189 /**
190 * Icons control.
191 */
192 const ICONS = 'icons';
193
194 /**
195 * Gallery control.
196 */
197 const GALLERY = 'gallery';
198
199 /**
200 * Structure control.
201 */
202 const STRUCTURE = 'structure';
203
204 /**
205 * Select2 control.
206 */
207 const SELECT2 = 'select2';
208
209 /**
210 * Date/Time control.
211 */
212 const DATE_TIME = 'date_time';
213
214 /**
215 * Box shadow control.
216 */
217 const BOX_SHADOW = 'box_shadow';
218
219 /**
220 * Text shadow control.
221 */
222 const TEXT_SHADOW = 'text_shadow';
223
224 /**
225 * Entrance animation control.
226 */
227 const ANIMATION = 'animation';
228
229 /**
230 * Hover animation control.
231 */
232 const HOVER_ANIMATION = 'hover_animation';
233
234 /**
235 * Exit animation control.
236 */
237 const EXIT_ANIMATION = 'exit_animation';
238
239 /**
240 * Gaps control.
241 */
242 const GAPS = 'gaps';
243
244 /**
245 * Controls.
246 *
247 * Holds the list of all the controls. Default is `null`.
248 *
249 * @since 1.0.0
250 * @access private
251 *
252 * @var Base_Control[]
253 */
254 private $controls = null;
255
256 /**
257 * Control groups.
258 *
259 * Holds the list of all the control groups. Default is an empty array.
260 *
261 * @since 1.0.0
262 * @access private
263 *
264 * @var Group_Control_Base[]
265 */
266 private $control_groups = [];
267
268 /**
269 * Control stacks.
270 *
271 * Holds the list of all the control stacks. Default is an empty array.
272 *
273 * @since 1.0.0
274 * @access private
275 *
276 * @var array
277 */
278 private $stacks = [];
279
280 /**
281 * Tabs.
282 *
283 * Holds the list of all the tabs.
284 *
285 * @since 1.0.0
286 * @access private
287 * @static
288 *
289 * @var array
290 */
291 private static $tabs;
292
293 /**
294 * Init tabs.
295 *
296 * Initialize control tabs.
297 *
298 * @since 1.6.0
299 * @access private
300 * @static
301 */
302 private static function init_tabs() {
303 self::$tabs = [
304 self::TAB_CONTENT => esc_html__( 'Content', 'elementor' ),
305 self::TAB_STYLE => esc_html__( 'Style', 'elementor' ),
306 self::TAB_ADVANCED => esc_html__( 'Advanced', 'elementor' ),
307 self::TAB_RESPONSIVE => esc_html__( 'Responsive', 'elementor' ),
308 self::TAB_LAYOUT => esc_html__( 'Layout', 'elementor' ),
309 self::TAB_SETTINGS => esc_html__( 'Settings', 'elementor' ),
310 ];
311 }
312
313 /**
314 * Get tabs.
315 *
316 * Retrieve the tabs of the current control.
317 *
318 * @since 1.6.0
319 * @access public
320 * @static
321 *
322 * @return array Control tabs.
323 */
324 public static function get_tabs() {
325 if ( ! self::$tabs ) {
326 self::init_tabs();
327 }
328
329 return self::$tabs;
330 }
331
332 /**
333 * Add tab.
334 *
335 * This method adds a new tab to the current control.
336 *
337 * @since 1.6.0
338 * @access public
339 * @static
340 *
341 * @param string $tab_name Tab name.
342 * @param string $tab_label Tab label.
343 */
344 public static function add_tab( $tab_name, $tab_label = '' ) {
345 if ( ! self::$tabs ) {
346 self::init_tabs();
347 }
348
349 if ( isset( self::$tabs[ $tab_name ] ) ) {
350 return;
351 }
352
353 self::$tabs[ $tab_name ] = $tab_label;
354 }
355
356 public static function get_groups_names() {
357 // Group name must use "-" instead of "_"
358 return [
359 'background',
360 'border',
361 'typography',
362 'image-size',
363 'box-shadow',
364 'css-filter',
365 'text-shadow',
366 'flex-container',
367 'grid-container',
368 'flex-item',
369 'text-stroke',
370 ];
371 }
372
373 public static function get_controls_names() {
374 return [
375 self::TEXT,
376 self::NUMBER,
377 self::TEXTAREA,
378 self::SELECT,
379 self::SWITCHER,
380
381 self::BUTTON,
382 self::HIDDEN,
383 self::HEADING,
384 self::RAW_HTML,
385 self::POPOVER_TOGGLE,
386 self::SECTION,
387 self::TAB,
388 self::TABS,
389 self::DIVIDER,
390 self::DEPRECATED_NOTICE,
391
392 self::COLOR,
393 self::MEDIA,
394 self::SLIDER,
395 self::DIMENSIONS,
396 self::CHOOSE,
397 self::WYSIWYG,
398 self::CODE,
399 self::FONT,
400 self::IMAGE_DIMENSIONS,
401 self::GAPS,
402
403 self::WP_WIDGET,
404
405 self::URL,
406 self::REPEATER,
407 self::ICON,
408 self::ICONS,
409 self::GALLERY,
410 self::STRUCTURE,
411 self::SELECT2,
412 self::DATE_TIME,
413 self::BOX_SHADOW,
414 self::TEXT_SHADOW,
415 self::ANIMATION,
416 self::HOVER_ANIMATION,
417 self::EXIT_ANIMATION,
418 ];
419 }
420
421 /**
422 * Register controls.
423 *
424 * This method creates a list of all the supported controls by requiring the
425 * control files and initializing each one of them.
426 *
427 * The list of supported controls includes the regular controls and the group
428 * controls.
429 *
430 * External developers can register new controls by hooking to the
431 * `elementor/controls/controls_registered` action.
432 *
433 * @since 3.1.0
434 * @access private
435 */
436 private function register_controls() {
437 $this->controls = [];
438
439 foreach ( self::get_controls_names() as $control_id ) {
440 $control_class_id = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $control_id ) ) );
441 $class_name = __NAMESPACE__ . '\Control_' . $control_class_id;
442
443 $this->register( new $class_name() );
444 }
445
446 // Group Controls
447 foreach ( self::get_groups_names() as $group_name ) {
448 $group_class_id = str_replace( ' ', '_', ucwords( str_replace( '-', ' ', $group_name ) ) );
449 $class_name = __NAMESPACE__ . '\Group_Control_' . $group_class_id;
450
451 $this->control_groups[ $group_name ] = new $class_name();
452 }
453
454 /**
455 * After controls registered.
456 *
457 * Fires after Elementor controls are registered.
458 *
459 * @since 1.0.0
460 * @deprecated 3.5.0 Use `elementor/controls/register` hook instead.
461 *
462 * @param Controls_Manager $this The controls manager.
463 */
464 // TODO: Uncomment when Pro uses the new hook.
465 //Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->do_deprecated_action(
466 // 'elementor/controls/controls_registered',
467 // [ $this ],
468 // '3.5.0',
469 // 'elementor/controls/register'
470 //);
471
472 do_action( 'elementor/controls/controls_registered', $this );
473
474 /**
475 * After controls registered.
476 *
477 * Fires after Elementor controls are registered.
478 *
479 * @since 3.5.0
480 *
481 * @param Controls_Manager $this The controls manager.
482 */
483 do_action( 'elementor/controls/register', $this );
484 }
485
486 /**
487 * Register control.
488 *
489 * This method adds a new control to the controls list. It adds any given
490 * control to any given control instance.
491 *
492 * @since 1.0.0
493 * @access public
494 * @deprecated 3.5.0 Use `$this->register()` instead.
495 *
496 * @param string $control_id Control ID.
497 * @param Base_Control $control_instance Control instance, usually the
498 * current instance.
499 */
500 public function register_control( $control_id, Base_Control $control_instance ) {
501 // TODO: Uncomment when Pro uses the new hook.
502 //Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
503 // __METHOD__,
504 // '3.5.0',
505 // 'register'
506 //);
507
508 $this->register( $control_instance, $control_id );
509 }
510
511 /**
512 * Register control.
513 *
514 * This method adds a new control to the controls list. It adds any given
515 * control to any given control instance.
516 *
517 * @since 3.5.0
518 * @access public
519 *
520 * @param Base_Control $control_instance Control instance, usually the current instance.
521 * @param string $control_id Control ID. Deprecated parameter.
522 *
523 * @return void
524 */
525 public function register( Base_Control $control_instance, $control_id = null ) {
526
527 // TODO: For BC. Remove in the future.
528 if ( $control_id ) {
529 Plugin::instance()->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_argument(
530 '$control_id', '3.5.0'
531 );
532 } else {
533 $control_id = $control_instance->get_type();
534 }
535
536 $this->controls[ $control_id ] = $control_instance;
537 }
538
539 /**
540 * Unregister control.
541 *
542 * This method removes control from the controls list.
543 *
544 * @since 1.0.0
545 * @access public
546 * @deprecated 3.5.0 Use `$this->unregister()` instead.
547 *
548 * @param string $control_id Control ID.
549 *
550 * @return bool True if the control was removed, False otherwise.
551 */
552 public function unregister_control( $control_id ) {
553 Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function(
554 __METHOD__,
555 '3.5.0',
556 'unregister'
557 );
558
559 return $this->unregister( $control_id );
560 }
561
562 /**
563 * Unregister control.
564 *
565 * This method removes control from the controls list.
566 *
567 * @since 3.5.0
568 * @access public
569 *
570 * @param string $control_id Control ID.
571 *
572 * @return bool Whether the controls has been unregistered.
573 */
574 public function unregister( $control_id ) {
575 if ( ! isset( $this->controls[ $control_id ] ) ) {
576 return false;
577 }
578
579 unset( $this->controls[ $control_id ] );
580
581 return true;
582 }
583
584 /**
585 * Get controls.
586 *
587 * Retrieve the controls list from the current instance.
588 *
589 * @since 1.0.0
590 * @access public
591 *
592 * @return Base_Control[] Controls list.
593 */
594 public function get_controls() {
595 if ( null === $this->controls ) {
596 $this->register_controls();
597 }
598
599 return $this->controls;
600 }
601
602 /**
603 * Get control.
604 *
605 * Retrieve a specific control from the current controls instance.
606 *
607 * @since 1.0.0
608 * @access public
609 *
610 * @param string $control_id Control ID.
611 *
612 * @return bool|Base_Control Control instance, or False otherwise.
613 */
614 public function get_control( $control_id ) {
615 $controls = $this->get_controls();
616
617 return isset( $controls[ $control_id ] ) ? $controls[ $control_id ] : false;
618 }
619
620 /**
621 * Get controls data.
622 *
623 * Retrieve all the registered controls and all the data for each control.
624 *
625 * @since 1.0.0
626 * @access public
627 *
628 * @return array {
629 * Control data.
630 *
631 * @type array $name Control data.
632 * }
633 */
634 public function get_controls_data() {
635 $controls_data = [];
636
637 foreach ( $this->get_controls() as $name => $control ) {
638 $controls_data[ $name ] = $control->get_settings();
639 }
640
641 return $controls_data;
642 }
643
644 /**
645 * Render controls.
646 *
647 * Generate the final HTML for all the registered controls using the element
648 * template.
649 *
650 * @since 1.0.0
651 * @access public
652 */
653 public function render_controls() {
654 foreach ( $this->get_controls() as $control ) {
655 $control->print_template();
656 }
657 }
658
659 /**
660 * Get control groups.
661 *
662 * Retrieve a specific group for a given ID, or a list of all the control
663 * groups.
664 *
665 * If the given group ID is wrong, it will return `null`. When the ID valid,
666 * it will return the group control instance. When no ID was given, it will
667 * return all the control groups.
668 *
669 * @since 1.0.10
670 * @access public
671 *
672 * @param string $id Optional. Group ID. Default is null.
673 *
674 * @return null|Group_Control_Base|Group_Control_Base[]
675 */
676 public function get_control_groups( $id = null ) {
677 if ( $id ) {
678 return isset( $this->control_groups[ $id ] ) ? $this->control_groups[ $id ] : null;
679 }
680
681 return $this->control_groups;
682 }
683
684 /**
685 * Add group control.
686 *
687 * This method adds a new group control to the control groups list. It adds
688 * any given group control to any given group control instance.
689 *
690 * @since 1.0.0
691 * @access public
692 *
693 * @param string $id Group control ID.
694 * @param Group_Control_Base $instance Group control instance, usually the
695 * current instance.
696 *
697 * @return Group_Control_Base Group control instance.
698 */
699 public function add_group_control( $id, $instance ) {
700 $this->control_groups[ $id ] = $instance;
701
702 return $instance;
703 }
704
705 /**
706 * Enqueue control scripts and styles.
707 *
708 * Used to register and enqueue custom scripts and styles used by the control.
709 *
710 * @since 1.0.0
711 * @access public
712 */
713 public function enqueue_control_scripts() {
714 foreach ( $this->get_controls() as $control ) {
715 $control->enqueue();
716 }
717 }
718
719 /**
720 * Open new stack.
721 *
722 * This method adds a new stack to the control stacks list. It adds any
723 * given stack to the current control instance.
724 *
725 * @since 1.0.0
726 * @access public
727 *
728 * @param Controls_Stack $controls_stack Controls stack.
729 */
730 public function open_stack( Controls_Stack $controls_stack ) {
731 $stack_id = $controls_stack->get_unique_name();
732
733 $this->stacks[ $stack_id ] = [
734 'tabs' => [],
735 'controls' => [],
736 ];
737 }
738
739 /**
740 * Remove existing stack from the stacks cache
741 *
742 * Removes the stack of a passed instance from the Controls Manager's stacks cache.
743 *
744 * @param Controls_Stack $controls_stack
745 * @return void
746 */
747 public function delete_stack( Controls_Stack $controls_stack ) {
748 $stack_id = $controls_stack->get_unique_name();
749
750 unset( $this->stacks[ $stack_id ] );
751 }
752
753 /**
754 * Add control to stack.
755 *
756 * This method adds a new control to the stack.
757 *
758 * @since 1.0.0
759 * @access public
760 *
761 * @param Controls_Stack $element Element stack.
762 * @param string $control_id Control ID.
763 * @param array $control_data Control data.
764 * @param array $options Optional. Control additional options.
765 * Default is an empty array.
766 *
767 * @return bool True if control added, False otherwise.
768 */
769 public function add_control_to_stack( Controls_Stack $element, $control_id, $control_data, $options = [] ) {
770 $default_options = [
771 'overwrite' => false,
772 'index' => null,
773 ];
774
775 $options = array_merge( $default_options, $options );
776
777 $default_args = [
778 'type' => self::TEXT,
779 'tab' => self::TAB_CONTENT,
780 ];
781
782 $control_data['name'] = $control_id;
783
784 $control_data = array_merge( $default_args, $control_data );
785
786 $control_type_instance = $this->get_control( $control_data['type'] );
787
788 if ( ! $control_type_instance ) {
789 _doing_it_wrong( sprintf( '%1$s::%2$s', __CLASS__, __FUNCTION__ ), sprintf( 'Control type "%s" not found.', esc_html( $control_data['type'] ) ), '1.0.0' );
790 return false;
791 }
792
793 if ( $control_type_instance instanceof Base_Data_Control ) {
794 $control_default_value = $control_type_instance->get_default_value();
795
796 if ( is_array( $control_default_value ) ) {
797 $control_data['default'] = isset( $control_data['default'] ) ? array_merge( $control_default_value, $control_data['default'] ) : $control_default_value;
798 } else {
799 $control_data['default'] = isset( $control_data['default'] ) ? $control_data['default'] : $control_default_value;
800 }
801 }
802
803 $stack_id = $element->get_unique_name();
804
805 if ( ! $options['overwrite'] && isset( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
806 _doing_it_wrong( sprintf( '%1$s::%2$s', __CLASS__, __FUNCTION__ ), sprintf( 'Cannot redeclare control with same name "%s".', esc_html( $control_id ) ), '1.0.0' );
807
808 return false;
809 }
810
811 $tabs = self::get_tabs();
812
813 if ( ! isset( $tabs[ $control_data['tab'] ] ) ) {
814 $control_data['tab'] = $default_args['tab'];
815 }
816
817 $this->stacks[ $stack_id ]['tabs'][ $control_data['tab'] ] = $tabs[ $control_data['tab'] ];
818
819 $this->stacks[ $stack_id ]['controls'][ $control_id ] = $control_data;
820
821 if ( null !== $options['index'] ) {
822 $controls = $this->stacks[ $stack_id ]['controls'];
823
824 $controls_keys = array_keys( $controls );
825
826 array_splice( $controls_keys, $options['index'], 0, $control_id );
827
828 $this->stacks[ $stack_id ]['controls'] = array_merge( array_flip( $controls_keys ), $controls );
829 }
830
831 return true;
832 }
833
834 /**
835 * Remove control from stack.
836 *
837 * This method removes a control a the stack.
838 *
839 * @since 1.0.0
840 * @access public
841 *
842 * @param string $stack_id Stack ID.
843 * @param array|string $control_id The ID of the control to remove.
844 *
845 * @return bool|\WP_Error True if the stack was removed, False otherwise.
846 */
847 public function remove_control_from_stack( $stack_id, $control_id ) {
848 if ( is_array( $control_id ) ) {
849 foreach ( $control_id as $id ) {
850 $this->remove_control_from_stack( $stack_id, $id );
851 }
852
853 return true;
854 }
855
856 if ( empty( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
857 return new \WP_Error( 'Cannot remove not-exists control.' );
858 }
859
860 unset( $this->stacks[ $stack_id ]['controls'][ $control_id ] );
861
862 return true;
863 }
864
865 /**
866 * Get control from stack.
867 *
868 * Retrieve a specific control for a given a specific stack.
869 *
870 * If the given control does not exist in the stack, or the stack does not
871 * exist, it will return `WP_Error`. Otherwise, it will retrieve the control
872 * from the stack.
873 *
874 * @since 1.1.0
875 * @access public
876 *
877 * @param string $stack_id Stack ID.
878 * @param string $control_id Control ID.
879 *
880 * @return array|\WP_Error The control, or an error.
881 */
882 public function get_control_from_stack( $stack_id, $control_id ) {
883 if ( empty( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
884 return new \WP_Error( 'Cannot get a not-exists control.' );
885 }
886
887 return $this->stacks[ $stack_id ]['controls'][ $control_id ];
888 }
889
890 /**
891 * Update control in stack.
892 *
893 * This method updates the control data for a given stack.
894 *
895 * @since 1.1.0
896 * @access public
897 *
898 * @param Controls_Stack $element Element stack.
899 * @param string $control_id Control ID.
900 * @param array $control_data Control data.
901 * @param array $options Optional. Control additional options.
902 * Default is an empty array.
903 *
904 * @return bool True if control updated, False otherwise.
905 */
906 public function update_control_in_stack( Controls_Stack $element, $control_id, $control_data, array $options = [] ) {
907 $old_control_data = $this->get_control_from_stack( $element->get_unique_name(), $control_id );
908
909 if ( is_wp_error( $old_control_data ) ) {
910 return false;
911 }
912
913 if ( ! empty( $options['recursive'] ) ) {
914 $control_data = array_replace_recursive( $old_control_data, $control_data );
915 } else {
916 $control_data = array_merge( $old_control_data, $control_data );
917 }
918
919 return $this->add_control_to_stack( $element, $control_id, $control_data, [
920 'overwrite' => true,
921 ] );
922 }
923
924 /**
925 * Get stacks.
926 *
927 * Retrieve a specific stack for the list of stacks.
928 *
929 * If the given stack is wrong, it will return `null`. When the stack valid,
930 * it will return the the specific stack. When no stack was given, it will
931 * return all the stacks.
932 *
933 * @since 1.7.1
934 * @access public
935 *
936 * @param string $stack_id Optional. stack ID. Default is null.
937 *
938 * @return null|array A list of stacks.
939 */
940 public function get_stacks( $stack_id = null ) {
941 if ( $stack_id ) {
942 if ( isset( $this->stacks[ $stack_id ] ) ) {
943 return $this->stacks[ $stack_id ];
944 }
945
946 return null;
947 }
948
949 return $this->stacks;
950 }
951
952 /**
953 * Get element stack.
954 *
955 * Retrieve a specific stack for the list of stacks from the current instance.
956 *
957 * @since 1.0.0
958 * @access public
959 *
960 * @param Controls_Stack $controls_stack Controls stack.
961 *
962 * @return null|array Stack data if it exist, `null` otherwise.
963 */
964 public function get_element_stack( Controls_Stack $controls_stack ) {
965 $stack_id = $controls_stack->get_unique_name();
966
967 if ( ! isset( $this->stacks[ $stack_id ] ) ) {
968 return null;
969 }
970
971 return $this->stacks[ $stack_id ];
972 }
973
974 /**
975 * Add custom CSS controls.
976 *
977 * This method adds a new control for the "Custom CSS" feature. The free
978 * version of elementor uses this method to display an upgrade message to
979 * Elementor Pro.
980 *
981 * @since 1.0.0
982 * @access public
983 *
984 * @param Controls_Stack $controls_stack .
985 * @param string $tab
986 * @param array $additional_messages
987 *
988 */
989 public function add_custom_css_controls( Controls_Stack $controls_stack, $tab = self::TAB_ADVANCED, $additional_messages = [] ) {
990 $controls_stack->start_controls_section(
991 'section_custom_css_pro',
992 [
993 'label' => esc_html__( 'Custom CSS', 'elementor' ),
994 'tab' => $tab,
995 ]
996 );
997
998 $messages = [
999 esc_html__( 'Custom CSS lets you add CSS code to any widget, and see it render live right in the editor.', 'elementor' ),
1000 ];
1001
1002 if ( $additional_messages ) {
1003 $messages = array_merge( $messages, $additional_messages );
1004 }
1005
1006 $controls_stack->add_control(
1007 'custom_css_pro',
1008 [
1009 'type' => self::RAW_HTML,
1010 'raw' => $this->get_teaser_template( [
1011 'title' => esc_html__( 'Meet Our Custom CSS', 'elementor' ),
1012 'messages' => $messages,
1013 'link' => 'https://go.elementor.com/go-pro-custom-css/',
1014 ] ),
1015 ]
1016 );
1017
1018 $controls_stack->end_controls_section();
1019 }
1020
1021 /**
1022 * Add Page Transitions controls.
1023 *
1024 * This method adds a new control for the "Page Transitions" feature. The Core
1025 * version of elementor uses this method to display an upgrade message to
1026 * Elementor Pro.
1027 *
1028 * @param Controls_Stack $controls_stack .
1029 * @param string $tab
1030 * @param array $additional_messages
1031 *
1032 * @return void
1033 */
1034 public function add_page_transitions_controls( Controls_Stack $controls_stack, $tab = self::TAB_ADVANCED, $additional_messages = [] ) {
1035 $controls_stack->start_controls_section(
1036 'section_page_transitions_teaser',
1037 [
1038 'label' => esc_html__( 'Page Transitions', 'elementor' ),
1039 'tab' => $tab,
1040 ]
1041 );
1042
1043 $messages = [
1044 esc_html__( 'Page Transitions let you style entrance and exit animations between pages as well as display loader until your page assets load.', 'elementor' ),
1045 ];
1046
1047 if ( $additional_messages ) {
1048 $messages = array_merge( $messages, $additional_messages );
1049 }
1050
1051 $controls_stack->add_control(
1052 'page_transitions_teaser',
1053 [
1054 'type' => self::RAW_HTML,
1055 'raw' => $this->get_teaser_template( [
1056 'title' => esc_html__( 'Meet Page Transitions', 'elementor' ),
1057 'messages' => $messages,
1058 'link' => 'https://go.elementor.com/go-pro-page-transitions/',
1059 ] ),
1060 ]
1061 );
1062
1063 $controls_stack->end_controls_section();
1064 }
1065
1066 public function get_teaser_template( $texts ) {
1067 ob_start();
1068 ?>
1069 <div class="elementor-nerd-box">
1070 <img class="elementor-nerd-box-icon" src="<?php echo esc_url( ELEMENTOR_ASSETS_URL . 'images/go-pro.svg' ); ?>" loading="lazy" />
1071 <div class="elementor-nerd-box-title"><?php Utils::print_unescaped_internal_string( $texts['title'] ); ?></div>
1072 <?php foreach ( $texts['messages'] as $message ) { ?>
1073 <div class="elementor-nerd-box-message"><?php Utils::print_unescaped_internal_string( $message ); ?></div>
1074 <?php }
1075
1076 // Show the upgrade button only if the user doesn't have Pro.
1077 if ( $texts['link'] && ! Utils::has_pro() ) { ?>
1078 <a class="elementor-button go-pro" href="<?php echo esc_url( ( $texts['link'] ) ); ?>" target="_blank">
1079 <?php echo esc_html__( 'Upgrade Now', 'elementor' ); ?>
1080 </a>
1081 <?php } ?>
1082 </div>
1083 <?php
1084
1085 return ob_get_clean();
1086 }
1087
1088 /**
1089 * Get Responsive Control Device Suffix
1090 *
1091 * @param array $control
1092 * @return string $device suffix
1093 */
1094 public static function get_responsive_control_device_suffix( array $control ): string {
1095 if ( ! empty( $control['responsive']['max'] ) ) {
1096 $query_device = $control['responsive']['max'];
1097 } elseif ( ! empty( $control['responsive']['min'] ) ) {
1098 $query_device = $control['responsive']['min'];
1099 } else {
1100 return '';
1101 }
1102
1103 return 'desktop' === $query_device ? '' : '_' . $query_device;
1104 }
1105
1106 /**
1107 * Add custom attributes controls.
1108 *
1109 * This method adds a new control for the "Custom Attributes" feature. The free
1110 * version of elementor uses this method to display an upgrade message to
1111 * Elementor Pro.
1112 *
1113 * @since 2.8.3
1114 * @access public
1115 *
1116 * @param Controls_Stack $controls_stack.
1117 */
1118 public function add_custom_attributes_controls( Controls_Stack $controls_stack ) {
1119 $controls_stack->start_controls_section(
1120 'section_custom_attributes_pro',
1121 [
1122 'label' => esc_html__( 'Attributes', 'elementor' ),
1123 'tab' => self::TAB_ADVANCED,
1124 ]
1125 );
1126
1127 $controls_stack->add_control(
1128 'custom_attributes_pro',
1129 [
1130 'type' => self::RAW_HTML,
1131 'raw' => $this->get_teaser_template( [
1132 'title' => esc_html__( 'Meet Our Attributes', 'elementor' ),
1133 'messages' => [
1134 esc_html__( 'Attributes lets you add custom HTML attributes to any element.', 'elementor' ),
1135 ],
1136 'link' => 'https://go.elementor.com/go-pro-custom-attributes/',
1137 ] ),
1138 ]
1139 );
1140
1141 $controls_stack->end_controls_section();
1142 }
1143 }
1144