controls.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907
  1. <?php
  2. namespace Elementor;
  3. if ( ! defined( 'ABSPATH' ) ) {
  4. exit; // Exit if accessed directly.
  5. }
  6. /**
  7. * Elementor controls manager.
  8. *
  9. * Elementor controls manager handler class is responsible for registering and
  10. * initializing all the supported controls, both regular controls and the group
  11. * controls.
  12. *
  13. * @since 1.0.0
  14. */
  15. class Controls_Manager {
  16. /**
  17. * Content tab.
  18. */
  19. const TAB_CONTENT = 'content';
  20. /**
  21. * Style tab.
  22. */
  23. const TAB_STYLE = 'style';
  24. /**
  25. * Advanced tab.
  26. */
  27. const TAB_ADVANCED = 'advanced';
  28. /**
  29. * Responsive tab.
  30. */
  31. const TAB_RESPONSIVE = 'responsive';
  32. /**
  33. * Layout tab.
  34. */
  35. const TAB_LAYOUT = 'layout';
  36. /**
  37. * Settings tab.
  38. */
  39. const TAB_SETTINGS = 'settings';
  40. /**
  41. * Text control.
  42. */
  43. const TEXT = 'text';
  44. /**
  45. * Number control.
  46. */
  47. const NUMBER = 'number';
  48. /**
  49. * Textarea control.
  50. */
  51. const TEXTAREA = 'textarea';
  52. /**
  53. * Select control.
  54. */
  55. const SELECT = 'select';
  56. /**
  57. * Switcher control.
  58. */
  59. const SWITCHER = 'switcher';
  60. /**
  61. * Button control.
  62. */
  63. const BUTTON = 'button';
  64. /**
  65. * Hidden control.
  66. */
  67. const HIDDEN = 'hidden';
  68. /**
  69. * Heading control.
  70. */
  71. const HEADING = 'heading';
  72. /**
  73. * Raw HTML control.
  74. */
  75. const RAW_HTML = 'raw_html';
  76. /**
  77. * Popover Toggle control.
  78. */
  79. const POPOVER_TOGGLE = 'popover_toggle';
  80. /**
  81. * Section control.
  82. */
  83. const SECTION = 'section';
  84. /**
  85. * Tab control.
  86. */
  87. const TAB = 'tab';
  88. /**
  89. * Tabs control.
  90. */
  91. const TABS = 'tabs';
  92. /**
  93. * Divider control.
  94. */
  95. const DIVIDER = 'divider';
  96. /**
  97. * Color control.
  98. */
  99. const COLOR = 'color';
  100. /**
  101. * Media control.
  102. */
  103. const MEDIA = 'media';
  104. /**
  105. * Slider control.
  106. */
  107. const SLIDER = 'slider';
  108. /**
  109. * Dimensions control.
  110. */
  111. const DIMENSIONS = 'dimensions';
  112. /**
  113. * Choose control.
  114. */
  115. const CHOOSE = 'choose';
  116. /**
  117. * WYSIWYG control.
  118. */
  119. const WYSIWYG = 'wysiwyg';
  120. /**
  121. * Code control.
  122. */
  123. const CODE = 'code';
  124. /**
  125. * Font control.
  126. */
  127. const FONT = 'font';
  128. /**
  129. * Image dimensions control.
  130. */
  131. const IMAGE_DIMENSIONS = 'image_dimensions';
  132. /**
  133. * WordPress widget control.
  134. */
  135. const WP_WIDGET = 'wp_widget';
  136. /**
  137. * URL control.
  138. */
  139. const URL = 'url';
  140. /**
  141. * Repeater control.
  142. */
  143. const REPEATER = 'repeater';
  144. /**
  145. * Icon control.
  146. */
  147. const ICON = 'icon';
  148. /**
  149. * Gallery control.
  150. */
  151. const GALLERY = 'gallery';
  152. /**
  153. * Structure control.
  154. */
  155. const STRUCTURE = 'structure';
  156. /**
  157. * Select2 control.
  158. */
  159. const SELECT2 = 'select2';
  160. /**
  161. * Date/Time control.
  162. */
  163. const DATE_TIME = 'date_time';
  164. /**
  165. * Box shadow control.
  166. */
  167. const BOX_SHADOW = 'box_shadow';
  168. /**
  169. * Text shadow control.
  170. */
  171. const TEXT_SHADOW = 'text_shadow';
  172. /**
  173. * Entrance animation control.
  174. */
  175. const ANIMATION = 'animation';
  176. /**
  177. * Hover animation control.
  178. */
  179. const HOVER_ANIMATION = 'hover_animation';
  180. /**
  181. * Order control.
  182. *
  183. * @deprecated 2.0.0
  184. */
  185. const ORDER = 'order';
  186. /**
  187. * Controls.
  188. *
  189. * Holds the list of all the controls. Default is `null`.
  190. *
  191. * @since 1.0.0
  192. * @access private
  193. *
  194. * @var Base_Control[]
  195. */
  196. private $controls = null;
  197. /**
  198. * Control groups.
  199. *
  200. * Holds the list of all the control groups. Default is an empty array.
  201. *
  202. * @since 1.0.0
  203. * @access private
  204. *
  205. * @var Group_Control_Base[]
  206. */
  207. private $control_groups = [];
  208. /**
  209. * Control stacks.
  210. *
  211. * Holds the list of all the control stacks. Default is an empty array.
  212. *
  213. * @since 1.0.0
  214. * @access private
  215. *
  216. * @var array
  217. */
  218. private $stacks = [];
  219. /**
  220. * Tabs.
  221. *
  222. * Holds the list of all the tabs.
  223. *
  224. * @since 1.0.0
  225. * @access private
  226. * @static
  227. *
  228. * @var array
  229. */
  230. private static $tabs;
  231. /**
  232. * Init tabs.
  233. *
  234. * Initialize control tabs.
  235. *
  236. * @since 1.6.0
  237. * @access private
  238. * @static
  239. */
  240. private static function init_tabs() {
  241. self::$tabs = [
  242. self::TAB_CONTENT => __( 'Content', 'elementor' ),
  243. self::TAB_STYLE => __( 'Style', 'elementor' ),
  244. self::TAB_ADVANCED => __( 'Advanced', 'elementor' ),
  245. self::TAB_RESPONSIVE => __( 'Responsive', 'elementor' ),
  246. self::TAB_LAYOUT => __( 'Layout', 'elementor' ),
  247. self::TAB_SETTINGS => __( 'Settings', 'elementor' ),
  248. ];
  249. }
  250. /**
  251. * Get tabs.
  252. *
  253. * Retrieve the tabs of the current control.
  254. *
  255. * @since 1.6.0
  256. * @access public
  257. * @static
  258. *
  259. * @return array Control tabs.
  260. */
  261. public static function get_tabs() {
  262. if ( ! self::$tabs ) {
  263. self::init_tabs();
  264. }
  265. return self::$tabs;
  266. }
  267. /**
  268. * Add tab.
  269. *
  270. * This method adds a new tab to the current control.
  271. *
  272. * @since 1.6.0
  273. * @access public
  274. * @static
  275. *
  276. * @param string $tab_name Tab name.
  277. * @param string $tab_label Tab label.
  278. */
  279. public static function add_tab( $tab_name, $tab_label ) {
  280. if ( ! self::$tabs ) {
  281. self::init_tabs();
  282. }
  283. if ( isset( self::$tabs[ $tab_name ] ) ) {
  284. return;
  285. }
  286. self::$tabs[ $tab_name ] = $tab_label;
  287. }
  288. /**
  289. * Register controls.
  290. *
  291. * This method creates a list of all the supported controls by requiring the
  292. * control files and initializing each one of them.
  293. *
  294. * The list of supported controls includes the regular controls and the group
  295. * controls.
  296. *
  297. * External developers can register new controls by hooking to the
  298. * `elementor/controls/controls_registered` action.
  299. *
  300. * @since 1.0.0
  301. * @access private
  302. */
  303. private function register_controls() {
  304. $this->controls = [];
  305. $available_controls = [
  306. self::TEXT,
  307. self::NUMBER,
  308. self::TEXTAREA,
  309. self::SELECT,
  310. self::SWITCHER,
  311. self::BUTTON,
  312. self::HIDDEN,
  313. self::HEADING,
  314. self::RAW_HTML,
  315. self::POPOVER_TOGGLE,
  316. self::SECTION,
  317. self::TAB,
  318. self::TABS,
  319. self::DIVIDER,
  320. self::COLOR,
  321. self::MEDIA,
  322. self::SLIDER,
  323. self::DIMENSIONS,
  324. self::CHOOSE,
  325. self::WYSIWYG,
  326. self::CODE,
  327. self::FONT,
  328. self::IMAGE_DIMENSIONS,
  329. self::WP_WIDGET,
  330. self::URL,
  331. self::REPEATER,
  332. self::ICON,
  333. self::GALLERY,
  334. self::STRUCTURE,
  335. self::SELECT2,
  336. self::DATE_TIME,
  337. self::BOX_SHADOW,
  338. self::TEXT_SHADOW,
  339. self::ANIMATION,
  340. self::HOVER_ANIMATION,
  341. self::ORDER,
  342. ];
  343. foreach ( $available_controls as $control_id ) {
  344. $control_filename = str_replace( '_', '-', $control_id );
  345. $control_filename = ELEMENTOR_PATH . "includes/controls/{$control_filename}.php";
  346. require( $control_filename );
  347. $class_name = __NAMESPACE__ . '\Control_' . ucwords( $control_id );
  348. $this->register_control( $control_id, new $class_name() );
  349. }
  350. // Group Controls
  351. $this->control_groups['background'] = new Group_Control_Background();
  352. $this->control_groups['border'] = new Group_Control_Border();
  353. $this->control_groups['typography'] = new Group_Control_Typography();
  354. $this->control_groups['image-size'] = new Group_Control_Image_Size();
  355. $this->control_groups['box-shadow'] = new Group_Control_Box_Shadow();
  356. $this->control_groups['css-filter'] = new Group_Control_Css_Filter();
  357. $this->control_groups['text-shadow'] = new Group_Control_Text_Shadow();
  358. /**
  359. * After controls registered.
  360. *
  361. * Fires after Elementor controls are registered.
  362. *
  363. * @since 1.0.0
  364. *
  365. * @param Controls_Manager $this The controls manager.
  366. */
  367. do_action( 'elementor/controls/controls_registered', $this );
  368. }
  369. /**
  370. * Register control.
  371. *
  372. * This method adds a new control to the controls list. It adds any given
  373. * control to any given control instance.
  374. *
  375. * @since 1.0.0
  376. * @access public
  377. *
  378. * @param string $control_id Control ID.
  379. * @param Base_Control $control_instance Control instance, usually the
  380. * current instance.
  381. */
  382. public function register_control( $control_id, Base_Control $control_instance ) {
  383. $this->controls[ $control_id ] = $control_instance;
  384. }
  385. /**
  386. * Unregister control.
  387. *
  388. * This method removes control from the controls list.
  389. *
  390. * @since 1.0.0
  391. * @access public
  392. *
  393. * @param string $control_id Control ID.
  394. *
  395. * @return bool True if the control was removed, False otherwise.
  396. */
  397. public function unregister_control( $control_id ) {
  398. if ( ! isset( $this->controls[ $control_id ] ) ) {
  399. return false;
  400. }
  401. unset( $this->controls[ $control_id ] );
  402. return true;
  403. }
  404. /**
  405. * Get controls.
  406. *
  407. * Retrieve the controls list from the current instance.
  408. *
  409. * @since 1.0.0
  410. * @access public
  411. *
  412. * @return Base_Control[] Controls list.
  413. */
  414. public function get_controls() {
  415. if ( null === $this->controls ) {
  416. $this->register_controls();
  417. }
  418. return $this->controls;
  419. }
  420. /**
  421. * Get control.
  422. *
  423. * Retrieve a specific control from the current controls instance.
  424. *
  425. * @since 1.0.0
  426. * @access public
  427. *
  428. * @param string $control_id Control ID.
  429. *
  430. * @return bool|Base_Control Control instance, or False otherwise.
  431. */
  432. public function get_control( $control_id ) {
  433. $controls = $this->get_controls();
  434. return isset( $controls[ $control_id ] ) ? $controls[ $control_id ] : false;
  435. }
  436. /**
  437. * Get controls data.
  438. *
  439. * Retrieve all the registered controls and all the data for each control.
  440. *
  441. * @since 1.0.0
  442. * @access public
  443. *
  444. * @return array {
  445. * Control data.
  446. *
  447. * @type array $name Control data.
  448. * }
  449. */
  450. public function get_controls_data() {
  451. $controls_data = [];
  452. foreach ( $this->get_controls() as $name => $control ) {
  453. $controls_data[ $name ] = $control->get_settings();
  454. }
  455. return $controls_data;
  456. }
  457. /**
  458. * Render controls.
  459. *
  460. * Generate the final HTML for all the registered controls using the element
  461. * template.
  462. *
  463. * @since 1.0.0
  464. * @access public
  465. */
  466. public function render_controls() {
  467. foreach ( $this->get_controls() as $control ) {
  468. $control->print_template();
  469. }
  470. }
  471. /**
  472. * Get control groups.
  473. *
  474. * Retrieve a specific group for a given ID, or a list of all the control
  475. * groups.
  476. *
  477. * If the given group ID is wrong, it will return `null`. When the ID valid,
  478. * it will return the group control instance. When no ID was given, it will
  479. * return all the control groups.
  480. *
  481. * @since 1.0.10
  482. * @access public
  483. *
  484. * @param string $id Optional. Group ID. Default is null.
  485. *
  486. * @return null|Group_Control_Base|Group_Control_Base[]
  487. */
  488. public function get_control_groups( $id = null ) {
  489. if ( $id ) {
  490. return isset( $this->control_groups[ $id ] ) ? $this->control_groups[ $id ] : null;
  491. }
  492. return $this->control_groups;
  493. }
  494. /**
  495. * Add group control.
  496. *
  497. * This method adds a new group control to the control groups list. It adds
  498. * any given group control to any given group control instance.
  499. *
  500. * @since 1.0.0
  501. * @access public
  502. *
  503. * @param string $id Group control ID.
  504. * @param Group_Control_Base[] $instance Group control instance, usually the
  505. * current instance.
  506. *
  507. * @return Group_Control_Base[] Group control instance.
  508. */
  509. public function add_group_control( $id, $instance ) {
  510. $this->control_groups[ $id ] = $instance;
  511. return $instance;
  512. }
  513. /**
  514. * Enqueue control scripts and styles.
  515. *
  516. * Used to register and enqueue custom scripts and styles used by the control.
  517. *
  518. * @since 1.0.0
  519. * @access public
  520. */
  521. public function enqueue_control_scripts() {
  522. foreach ( $this->get_controls() as $control ) {
  523. $control->enqueue();
  524. }
  525. }
  526. /**
  527. * Open new stack.
  528. *
  529. * This method adds a new stack to the control stacks list. It adds any
  530. * given stack to the current control instance.
  531. *
  532. * @since 1.0.0
  533. * @access public
  534. *
  535. * @param Controls_Stack $controls_stack Controls stack.
  536. */
  537. public function open_stack( Controls_Stack $controls_stack ) {
  538. $stack_id = $controls_stack->get_unique_name();
  539. $this->stacks[ $stack_id ] = [
  540. 'tabs' => [],
  541. 'controls' => [],
  542. ];
  543. }
  544. /**
  545. * Add control to stack.
  546. *
  547. * This method adds a new control to the stack.
  548. *
  549. * @since 1.0.0
  550. * @access public
  551. *
  552. * @param Controls_Stack $element Element stack.
  553. * @param string $control_id Control ID.
  554. * @param array $control_data Control data.
  555. * @param array $options Optional. Control additional options.
  556. * Default is an empty array.
  557. *
  558. * @return bool True if control added, False otherwise.
  559. */
  560. public function add_control_to_stack( Controls_Stack $element, $control_id, $control_data, $options = [] ) {
  561. if ( ! is_array( $options ) ) {
  562. _deprecated_argument( __FUNCTION__, '1.7.0', sprintf( 'Use `[ \'overwrite\' => %s ]` instead.', var_export( $options, true ) ) );
  563. $options = [
  564. 'overwrite' => $options,
  565. ];
  566. }
  567. $default_options = [
  568. 'overwrite' => false,
  569. 'index' => null,
  570. ];
  571. $options = array_merge( $default_options, $options );
  572. $default_args = [
  573. 'type' => self::TEXT,
  574. 'tab' => self::TAB_CONTENT,
  575. ];
  576. $control_data['name'] = $control_id;
  577. $control_data = array_merge( $default_args, $control_data );
  578. $control_type_instance = $this->get_control( $control_data['type'] );
  579. if ( ! $control_type_instance ) {
  580. _doing_it_wrong( sprintf( '%1$s::%2$s', __CLASS__, __FUNCTION__ ), sprintf( 'Control type "%s" not found.', $control_data['type'] ), '1.0.0' );
  581. return false;
  582. }
  583. if ( $control_type_instance instanceof Base_Data_Control ) {
  584. $control_default_value = $control_type_instance->get_default_value();
  585. if ( is_array( $control_default_value ) ) {
  586. $control_data['default'] = isset( $control_data['default'] ) ? array_merge( $control_default_value, $control_data['default'] ) : $control_default_value;
  587. } else {
  588. $control_data['default'] = isset( $control_data['default'] ) ? $control_data['default'] : $control_default_value;
  589. }
  590. }
  591. $stack_id = $element->get_unique_name();
  592. if ( ! $options['overwrite'] && isset( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
  593. _doing_it_wrong( sprintf( '%1$s::%2$s', __CLASS__, __FUNCTION__ ), sprintf( 'Cannot redeclare control with same name "%s".', $control_id ), '1.0.0' );
  594. return false;
  595. }
  596. $tabs = self::get_tabs();
  597. if ( ! isset( $tabs[ $control_data['tab'] ] ) ) {
  598. $control_data['tab'] = $default_args['tab'];
  599. }
  600. $this->stacks[ $stack_id ]['tabs'][ $control_data['tab'] ] = $tabs[ $control_data['tab'] ];
  601. $this->stacks[ $stack_id ]['controls'][ $control_id ] = $control_data;
  602. if ( null !== $options['index'] ) {
  603. $controls = $this->stacks[ $stack_id ]['controls'];
  604. $controls_keys = array_keys( $controls );
  605. array_splice( $controls_keys, $options['index'], 0, $control_id );
  606. $this->stacks[ $stack_id ]['controls'] = array_merge( array_flip( $controls_keys ), $controls );
  607. }
  608. return true;
  609. }
  610. /**
  611. * Remove control from stack.
  612. *
  613. * This method removes a control a the stack.
  614. *
  615. * @since 1.0.0
  616. * @access public
  617. *
  618. * @param string $stack_id Stack ID.
  619. * @param string $control_id The ID of the control to remove.
  620. *
  621. * @return bool|\WP_Error True if the stack was removed, False otherwise.
  622. */
  623. public function remove_control_from_stack( $stack_id, $control_id ) {
  624. if ( is_array( $control_id ) ) {
  625. foreach ( $control_id as $id ) {
  626. $this->remove_control_from_stack( $stack_id, $id );
  627. }
  628. return true;
  629. }
  630. if ( empty( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
  631. return new \WP_Error( 'Cannot remove not-exists control.' );
  632. }
  633. unset( $this->stacks[ $stack_id ]['controls'][ $control_id ] );
  634. return true;
  635. }
  636. /**
  637. * Get control from stack.
  638. *
  639. * Retrieve a specific control for a given a specific stack.
  640. *
  641. * If the given control does not exist in the stack, or the stack does not
  642. * exist, it will return `WP_Error`. Otherwise, it will retrieve the control
  643. * from the stack.
  644. *
  645. * @since 1.1.0
  646. * @access public
  647. *
  648. * @param string $stack_id Stack ID.
  649. * @param string $control_id Control ID.
  650. *
  651. * @return array|\WP_Error The control, or an error.
  652. */
  653. public function get_control_from_stack( $stack_id, $control_id ) {
  654. if ( empty( $this->stacks[ $stack_id ]['controls'][ $control_id ] ) ) {
  655. return new \WP_Error( 'Cannot get a not-exists control.' );
  656. }
  657. return $this->stacks[ $stack_id ]['controls'][ $control_id ];
  658. }
  659. /**
  660. * Update control in stack.
  661. *
  662. * This method updates the control data for a given stack.
  663. *
  664. * @since 1.1.0
  665. * @access public
  666. *
  667. * @param Controls_Stack $element Element stack.
  668. * @param string $control_id Control ID.
  669. * @param array $control_data Control data.
  670. * @param array $options Optional. Control additional options.
  671. * Default is an empty array.
  672. *
  673. * @return bool True if control updated, False otherwise.
  674. */
  675. public function update_control_in_stack( Controls_Stack $element, $control_id, $control_data, array $options = [] ) {
  676. $old_control_data = $this->get_control_from_stack( $element->get_unique_name(), $control_id );
  677. if ( is_wp_error( $old_control_data ) ) {
  678. return false;
  679. }
  680. if ( ! empty( $options['recursive'] ) ) {
  681. $control_data = array_replace_recursive( $old_control_data, $control_data );
  682. } else {
  683. $control_data = array_merge( $old_control_data, $control_data );
  684. }
  685. return $this->add_control_to_stack( $element, $control_id, $control_data, [
  686. 'overwrite' => true,
  687. ] );
  688. }
  689. /**
  690. * Get stacks.
  691. *
  692. * Retrieve a specific stack for the list of stacks.
  693. *
  694. * If the given stack is wrong, it will return `null`. When the stack valid,
  695. * it will return the the specific stack. When no stack was given, it will
  696. * return all the stacks.
  697. *
  698. * @since 1.7.1
  699. * @access public
  700. *
  701. * @param string $stack_id Optional. stack ID. Default is null.
  702. *
  703. * @return null|array A list of stacks.
  704. */
  705. public function get_stacks( $stack_id = null ) {
  706. if ( $stack_id ) {
  707. if ( isset( $this->stacks[ $stack_id ] ) ) {
  708. return $this->stacks[ $stack_id ];
  709. }
  710. return null;
  711. }
  712. return $this->stacks;
  713. }
  714. /**
  715. * Get element stack.
  716. *
  717. * Retrieve a specific stack for the list of stacks from the current instance.
  718. *
  719. * @since 1.0.0
  720. * @access public
  721. *
  722. * @param Controls_Stack $controls_stack Controls stack.
  723. *
  724. * @return null|array Stack data if it exist, `null` otherwise.
  725. */
  726. public function get_element_stack( Controls_Stack $controls_stack ) {
  727. $stack_id = $controls_stack->get_unique_name();
  728. if ( ! isset( $this->stacks[ $stack_id ] ) ) {
  729. return null;
  730. }
  731. return $this->stacks[ $stack_id ];
  732. }
  733. /**
  734. * Add custom CSS controls.
  735. *
  736. * This method adds a new control for the "Custom CSS" feature. The free
  737. * version of elementor uses this method to display an upgrade message to
  738. * Elementor Pro.
  739. *
  740. * @since 1.0.0
  741. * @access public
  742. *
  743. * @param Element_Base $element The element.
  744. * @param string $tab The panel tab.
  745. */
  746. public function add_custom_css_controls( $element, $tab = self::TAB_ADVANCED ) {
  747. $element->start_controls_section(
  748. 'section_custom_css_pro',
  749. [
  750. 'label' => __( 'Custom CSS', 'elementor' ),
  751. 'tab' => $tab,
  752. ]
  753. );
  754. $element->add_control(
  755. 'custom_css_pro',
  756. [
  757. 'type' => self::RAW_HTML,
  758. 'raw' => '<div class="elementor-nerd-box">' .
  759. '<i class="elementor-nerd-box-icon eicon-hypster" aria-hidden="true"></i>
  760. <div class="elementor-nerd-box-title">' .
  761. __( 'Meet Our Custom CSS', 'elementor' ) .
  762. '</div>
  763. <div class="elementor-nerd-box-message">' .
  764. __( 'Custom CSS lets you add CSS code to any widget, and see it render live right in the editor.', 'elementor' ) .
  765. '</div>
  766. <div class="elementor-nerd-box-message">' .
  767. __( 'This feature is only available on Elementor Pro.', 'elementor' ) .
  768. '</div>
  769. <a class="elementor-nerd-box-link elementor-button elementor-button-default elementor-go-pro" href="' . Utils::get_pro_link( 'https://elementor.com/pro/?utm_source=panel-custom-css&utm_campaign=gopro&utm_medium=wp-dash' ) . '" target="_blank">' .
  770. __( 'Go Pro', 'elementor' ) .
  771. '</a>
  772. </div>',
  773. ]
  774. );
  775. $element->end_controls_section();
  776. }
  777. }