widget-base.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. <?php
  2. namespace Elementor;
  3. if ( ! defined( 'ABSPATH' ) ) {
  4. exit; // Exit if accessed directly.
  5. }
  6. /**
  7. * Elementor widget base.
  8. *
  9. * An abstract class to register new Elementor widgets. It extended the
  10. * `Element_Base` class to inherit its properties.
  11. *
  12. * This abstract class must be extended in order to register new widgets.
  13. *
  14. * @since 1.0.0
  15. * @abstract
  16. */
  17. abstract class Widget_Base extends Element_Base {
  18. /**
  19. * Whether the widget has content.
  20. *
  21. * Used in cases where the widget has no content. When widgets uses only
  22. * skins to display dynamic content generated on the server. For example the
  23. * posts widget in Elementor Pro. Default is true, the widget has content
  24. * template.
  25. *
  26. * @access protected
  27. *
  28. * @var bool
  29. */
  30. protected $_has_template_content = true;
  31. /**
  32. * Element edit tools.
  33. *
  34. * Holds all the edit tools of the element. For example: delete, duplicate etc.
  35. *
  36. * @access protected
  37. * @static
  38. *
  39. * @var array
  40. */
  41. protected static $_edit_tools;
  42. /**
  43. * Get element type.
  44. *
  45. * Retrieve the element type, in this case `widget`.
  46. *
  47. * @since 1.0.0
  48. * @access public
  49. * @static
  50. *
  51. * @return string The type.
  52. */
  53. public static function get_type() {
  54. return 'widget';
  55. }
  56. /**
  57. * Get widget icon.
  58. *
  59. * Retrieve the widget icon.
  60. *
  61. * @since 1.0.0
  62. * @access public
  63. *
  64. * @return string Widget icon.
  65. */
  66. public function get_icon() {
  67. return 'eicon-apps';
  68. }
  69. /**
  70. * Get widget keywords.
  71. *
  72. * Retrieve the widget keywords.
  73. *
  74. * @since 1.0.10
  75. * @access public
  76. *
  77. * @return array Widget keywords.
  78. */
  79. public function get_keywords() {
  80. return [];
  81. }
  82. /**
  83. * Get widget categories.
  84. *
  85. * Retrieve the widget categories.
  86. *
  87. * @since 1.0.10
  88. * @access public
  89. *
  90. * @return array Widget categories.
  91. */
  92. public function get_categories() {
  93. return [ 'general' ];
  94. }
  95. /**
  96. * Widget base constructor.
  97. *
  98. * Initializing the widget base class.
  99. *
  100. * @since 1.0.0
  101. * @access public
  102. *
  103. * @throws \Exception If arguments are missing when initializing a full widget
  104. * instance.
  105. *
  106. * @param array $data Widget data. Default is an empty array.
  107. * @param array|null $args Optional. Widget default arguments. Default is null.
  108. */
  109. public function __construct( $data = [], $args = null ) {
  110. parent::__construct( $data, $args );
  111. $is_type_instance = $this->is_type_instance();
  112. if ( ! $is_type_instance && null === $args ) {
  113. throw new \Exception( '`$args` argument is required when initializing a full widget instance.' );
  114. }
  115. if ( $is_type_instance ) {
  116. $this->_register_skins();
  117. $widget_name = $this->get_name();
  118. /**
  119. * Widget skin init.
  120. *
  121. * Fires when Elementor widget is being initialized.
  122. *
  123. * The dynamic portion of the hook name, `$widget_name`, refers to the widget name.
  124. *
  125. * @since 1.0.0
  126. *
  127. * @param Widget_Base $this The current widget.
  128. */
  129. do_action( "elementor/widget/{$widget_name}/skins_init", $this );
  130. }
  131. }
  132. /**
  133. * Get stack.
  134. *
  135. * Retrieve the widget stack of controls.
  136. *
  137. * @since 1.9.2
  138. * @access public
  139. *
  140. * @param bool $with_common_controls Optional. Whether to include the common controls. Default is true.
  141. *
  142. * @return array Widget stack of controls.
  143. */
  144. public function get_stack( $with_common_controls = true ) {
  145. $stack = parent::get_stack();
  146. if ( $with_common_controls && 'common' !== $this->get_unique_name() ) {
  147. /** @var Widget_Common $common_widget */
  148. $common_widget = Plugin::$instance->widgets_manager->get_widget_types( 'common' );
  149. $stack['controls'] = array_merge( $stack['controls'], $common_widget->get_controls() );
  150. $stack['tabs'] = array_merge( $stack['tabs'], $common_widget->get_tabs_controls() );
  151. }
  152. return $stack;
  153. }
  154. /**
  155. * Get widget controls pointer index.
  156. *
  157. * Retrieve widget pointer index where the next control should be added.
  158. *
  159. * While using injection point, it will return the injection point index. Otherwise index of the last control of the
  160. * current widget itself without the common controls, plus one.
  161. *
  162. * @since 1.9.2
  163. * @access public
  164. *
  165. * @return int Widget controls pointer index.
  166. */
  167. public function get_pointer_index() {
  168. $injection_point = $this->get_injection_point();
  169. if ( null !== $injection_point ) {
  170. return $injection_point['index'];
  171. }
  172. return count( $this->get_stack( false )['controls'] );
  173. }
  174. /**
  175. * Show in panel.
  176. *
  177. * Whether to show the widget in the panel or not. By default returns true.
  178. *
  179. * @since 1.0.0
  180. * @access public
  181. *
  182. * @return bool Whether to show the widget in the panel or not.
  183. */
  184. public function show_in_panel() {
  185. return true;
  186. }
  187. /**
  188. * Start widget controls section.
  189. *
  190. * Used to add a new section of controls to the widget. Regular controls and
  191. * skin controls.
  192. *
  193. * Note that when you add new controls to widgets they must be wrapped by
  194. * `start_controls_section()` and `end_controls_section()`.
  195. *
  196. * @since 1.0.0
  197. * @access public
  198. *
  199. * @param string $section_id Section ID.
  200. * @param array $args Section arguments.
  201. */
  202. public function start_controls_section( $section_id, array $args ) {
  203. parent::start_controls_section( $section_id, $args );
  204. static $is_first_section = true;
  205. if ( $is_first_section ) {
  206. $this->register_skin_control();
  207. $is_first_section = false;
  208. }
  209. }
  210. /**
  211. * Register the Skin Control if the widget has skins.
  212. *
  213. * An internal method that is used to add a skin control to the widget.
  214. * Added at the top of the controls section.
  215. *
  216. * @since 2.0.0
  217. * @access private
  218. */
  219. private function register_skin_control() {
  220. $skins = $this->get_skins();
  221. if ( ! empty( $skins ) ) {
  222. $skin_options = [];
  223. if ( $this->_has_template_content ) {
  224. $skin_options[''] = __( 'Default', 'elementor' );
  225. }
  226. foreach ( $skins as $skin_id => $skin ) {
  227. $skin_options[ $skin_id ] = $skin->get_title();
  228. }
  229. // Get the first item for default value
  230. $default_value = array_keys( $skin_options );
  231. $default_value = array_shift( $default_value );
  232. if ( 1 >= count( $skin_options ) ) {
  233. $this->add_control(
  234. '_skin',
  235. [
  236. 'label' => __( 'Skin', 'elementor' ),
  237. 'type' => Controls_Manager::HIDDEN,
  238. 'default' => $default_value,
  239. ]
  240. );
  241. } else {
  242. $this->add_control(
  243. '_skin',
  244. [
  245. 'label' => __( 'Skin', 'elementor' ),
  246. 'type' => Controls_Manager::SELECT,
  247. 'default' => $default_value,
  248. 'options' => $skin_options,
  249. ]
  250. );
  251. }
  252. }
  253. }
  254. /**
  255. * Get default edit tools.
  256. *
  257. * Retrieve the element default edit tools. Used to set initial tools.
  258. * By default the element has no edit tools.
  259. *
  260. * @since 1.0.0
  261. * @access protected
  262. * @static
  263. *
  264. * @return array Default edit tools.
  265. */
  266. protected static function get_default_edit_tools() {
  267. $widget_label = __( 'Widget', 'elementor' );
  268. $edit_tools = [
  269. 'edit' => [
  270. 'title' => __( 'Edit', 'elementor' ),
  271. 'icon' => 'edit',
  272. ],
  273. ];
  274. if ( self::is_edit_buttons_enabled() ) {
  275. $edit_tools += [
  276. 'duplicate' => [
  277. /* translators: %s: Widget label */
  278. 'title' => sprintf( __( 'Duplicate %s', 'elementor' ), $widget_label ),
  279. 'icon' => 'clone',
  280. ],
  281. 'remove' => [
  282. /* translators: %s: Widget label */
  283. 'title' => sprintf( __( 'Remove %s', 'elementor' ), $widget_label ),
  284. 'icon' => 'close',
  285. ],
  286. ];
  287. }
  288. return $edit_tools;
  289. }
  290. /**
  291. * Register widget skins.
  292. *
  293. * This method is activated while initializing the widget base class. It is
  294. * used to assign skins to widgets with `add_skin()` method.
  295. *
  296. * Usage:
  297. *
  298. * protected function _register_skins() {
  299. * $this->add_skin( new Skin_Classic( $this ) );
  300. * }
  301. *
  302. * @since 1.7.12
  303. * @access protected
  304. */
  305. protected function _register_skins() {}
  306. /**
  307. * Get initial config.
  308. *
  309. * Retrieve the current widget initial configuration.
  310. *
  311. * Adds more configuration on top of the controls list, the tabs assigned to
  312. * the control, element name, type, icon and more. This method also adds
  313. * widget type, keywords and categories.
  314. *
  315. * @since 1.0.10
  316. * @access protected
  317. *
  318. * @return array The initial widget config.
  319. */
  320. protected function _get_initial_config() {
  321. $config = [
  322. 'widget_type' => $this->get_name(),
  323. 'keywords' => $this->get_keywords(),
  324. 'categories' => $this->get_categories(),
  325. 'html_wrapper_class' => $this->get_html_wrapper_class(),
  326. 'show_in_panel' => $this->show_in_panel(),
  327. ];
  328. return array_merge( parent::_get_initial_config(), $config );
  329. }
  330. /**
  331. * Print widget content template.
  332. *
  333. * Used to generate the widget content template on the editor, using a
  334. * Backbone JavaScript template.
  335. *
  336. * @since 2.0.0
  337. * @access protected
  338. *
  339. * @param string $template_content Template content.
  340. */
  341. protected function print_template_content( $template_content ) {
  342. $this->render_edit_tools();
  343. ?>
  344. <div class="elementor-widget-container">
  345. <?php
  346. echo $template_content; // XSS ok.
  347. ?>
  348. </div>
  349. <?php
  350. }
  351. /**
  352. * Parse text editor.
  353. *
  354. * Parses the content from rich text editor with shortcodes, oEmbed and
  355. * filtered data.
  356. *
  357. * @since 1.0.0
  358. * @access protected
  359. *
  360. * @param string $content Text editor content.
  361. *
  362. * @return string Parsed content.
  363. */
  364. protected function parse_text_editor( $content ) {
  365. /** This filter is documented in wp-includes/widgets/class-wp-widget-text.php */
  366. $content = apply_filters( 'widget_text', $content, $this->get_settings() );
  367. $content = shortcode_unautop( $content );
  368. $content = do_shortcode( $content );
  369. $content = wptexturize( $content );
  370. if ( $GLOBALS['wp_embed'] instanceof \WP_Embed ) {
  371. $content = $GLOBALS['wp_embed']->autoembed( $content );
  372. }
  373. return $content;
  374. }
  375. /**
  376. * Get HTML wrapper class.
  377. *
  378. * Retrieve the widget container class. Can be used to override the
  379. * container class for specific widgets.
  380. *
  381. * @since 2.0.9
  382. * @access protected
  383. */
  384. protected function get_html_wrapper_class() {
  385. return 'elementor-widget-' . $this->get_name();
  386. }
  387. /**
  388. * Add widget render attributes.
  389. *
  390. * Used to add attributes to the current widget wrapper HTML tag.
  391. *
  392. * @since 1.0.0
  393. * @access protected
  394. */
  395. protected function _add_render_attributes() {
  396. parent::_add_render_attributes();
  397. $this->add_render_attribute(
  398. '_wrapper', 'class', [
  399. 'elementor-widget',
  400. $this->get_html_wrapper_class(),
  401. ]
  402. );
  403. $settings = $this->get_settings();
  404. $this->add_render_attribute( '_wrapper', 'data-element_type', $this->get_name() . '.' . ( ! empty( $settings['_skin'] ) ? $settings['_skin'] : 'default' ) );
  405. }
  406. /**
  407. * Render widget output on the frontend.
  408. *
  409. * Used to generate the final HTML displayed on the frontend.
  410. *
  411. * Note that if skin is selected, it will be rendered by the skin itself,
  412. * not the widget.
  413. *
  414. * @since 1.0.0
  415. * @access public
  416. */
  417. public function render_content() {
  418. /**
  419. * Before widget render content.
  420. *
  421. * Fires before Elementor widget is being rendered.
  422. *
  423. * @since 1.0.0
  424. *
  425. * @param Widget_Base $this The current widget.
  426. */
  427. do_action( 'elementor/widget/before_render_content', $this );
  428. if ( Plugin::$instance->editor->is_edit_mode() ) {
  429. $this->render_edit_tools();
  430. }
  431. ?>
  432. <div class="elementor-widget-container">
  433. <?php
  434. ob_start();
  435. $skin = $this->get_current_skin();
  436. if ( $skin ) {
  437. $skin->set_parent( $this );
  438. $skin->render();
  439. } else {
  440. $this->render();
  441. }
  442. $widget_content = ob_get_clean();
  443. /**
  444. * Render widget content.
  445. *
  446. * Filters the widget content before it's rendered.
  447. *
  448. * @since 1.0.0
  449. *
  450. * @param string $widget_content The content of the widget.
  451. * @param Widget_Base $this The widget.
  452. */
  453. $widget_content = apply_filters( 'elementor/widget/render_content', $widget_content, $this );
  454. echo $widget_content; // XSS ok.
  455. ?>
  456. </div>
  457. <?php
  458. }
  459. /**
  460. * Render widget plain content.
  461. *
  462. * Elementor saves the page content in a unique way, but it's not the way
  463. * WordPress saves data. This method is used to save generated HTML to the
  464. * database as plain content the WordPress way.
  465. *
  466. * When rendering plain content, it allows other WordPress plugins to
  467. * interact with the content - to search, check SEO and other purposes. It
  468. * also allows the site to keep working even if Elementor is deactivated.
  469. *
  470. * Note that if the widget uses shortcodes to display the data, the best
  471. * practice is to return the shortcode itself.
  472. *
  473. * Also note that if the widget don't display any content it should return
  474. * an empty string. For example Elementor Pro Form Widget uses this method
  475. * to return an empty string because there is no content to return. This way
  476. * if Elementor Pro will be deactivated there won't be any form to display.
  477. *
  478. * @since 1.0.0
  479. * @access public
  480. */
  481. public function render_plain_content() {
  482. $this->render_content();
  483. }
  484. /**
  485. * Before widget rendering.
  486. *
  487. * Used to add stuff before the widget `_wrapper` element.
  488. *
  489. * @since 1.0.0
  490. * @access public
  491. */
  492. public function before_render() {
  493. ?>
  494. <div <?php $this->print_render_attribute_string( '_wrapper' ); ?>>
  495. <?php
  496. }
  497. /**
  498. * After widget rendering.
  499. *
  500. * Used to add stuff after the widget `_wrapper` element.
  501. *
  502. * @since 1.0.0
  503. * @access public
  504. */
  505. public function after_render() {
  506. ?>
  507. </div>
  508. <?php
  509. }
  510. /**
  511. * Get the element raw data.
  512. *
  513. * Retrieve the raw element data, including the id, type, settings, child
  514. * elements and whether it is an inner element.
  515. *
  516. * The data with the HTML used always to display the data, but the Elementor
  517. * editor uses the raw data without the HTML in order not to render the data
  518. * again.
  519. *
  520. * @since 1.0.0
  521. * @access public
  522. *
  523. * @param bool $with_html_content Optional. Whether to return the data with
  524. * HTML content or without. Used for caching.
  525. * Default is false, without HTML.
  526. *
  527. * @return array Element raw data.
  528. */
  529. public function get_raw_data( $with_html_content = false ) {
  530. $data = parent::get_raw_data( $with_html_content );
  531. unset( $data['isInner'] );
  532. $data['widgetType'] = $this->get_data( 'widgetType' );
  533. if ( $with_html_content ) {
  534. ob_start();
  535. $this->render_content();
  536. $data['htmlCache'] = ob_get_clean();
  537. }
  538. return $data;
  539. }
  540. /**
  541. * Print widget content.
  542. *
  543. * Output the widget final HTML on the frontend.
  544. *
  545. * @since 1.0.0
  546. * @access protected
  547. */
  548. protected function _print_content() {
  549. $this->render_content();
  550. }
  551. /**
  552. * Get default data.
  553. *
  554. * Retrieve the default widget data. Used to reset the data on initialization.
  555. *
  556. * @since 1.0.0
  557. * @access protected
  558. *
  559. * @return array Default data.
  560. */
  561. protected function get_default_data() {
  562. $data = parent::get_default_data();
  563. $data['widgetType'] = '';
  564. return $data;
  565. }
  566. /**
  567. * Get default child type.
  568. *
  569. * Retrieve the widget child type based on element data.
  570. *
  571. * @since 1.0.0
  572. * @access protected
  573. *
  574. * @param array $element_data Widget ID.
  575. *
  576. * @return array|false Child type or false if it's not a valid widget.
  577. */
  578. protected function _get_default_child_type( array $element_data ) {
  579. return Plugin::$instance->elements_manager->get_element_types( 'section' );
  580. }
  581. /**
  582. * Get repeater setting key.
  583. *
  584. * Retrieve the unique setting key for the current repeater item. Used to connect the current element in the
  585. * repeater to it's settings model and it's control in the panel.
  586. *
  587. * PHP usage (inside `Widget_Base::render()` method):
  588. *
  589. * $tabs = $this->get_settings( 'tabs' );
  590. * foreach ( $tabs as $index => $item ) {
  591. * $tab_title_setting_key = $this->get_repeater_setting_key( 'tab_title', 'tabs', $index );
  592. * $this->add_inline_editing_attributes( $tab_title_setting_key, 'none' );
  593. * echo '<div ' . $this->get_render_attribute_string( $tab_title_setting_key ) . '>' . $item['tab_title'] . '</div>';
  594. * }
  595. *
  596. * @since 1.8.0
  597. * @access protected
  598. *
  599. * @param string $setting_key The current setting key inside the repeater item (e.g. `tab_title`).
  600. * @param string $repeater_key The repeater key containing the array of all the items in the repeater (e.g. `tabs`).
  601. * @param int $repeater_item_index The current item index in the repeater array (e.g. `3`).
  602. *
  603. * @return string The repeater setting key (e.g. `tabs.3.tab_title`).
  604. */
  605. protected function get_repeater_setting_key( $setting_key, $repeater_key, $repeater_item_index ) {
  606. return implode( '.', [ $repeater_key, $repeater_item_index, $setting_key ] );
  607. }
  608. /**
  609. * Add inline editing attributes.
  610. *
  611. * Define specific area in the element to be editable inline. The element can have several areas, with this method
  612. * you can set the area inside the element that can be edited inline. You can also define the type of toolbar the
  613. * user will see, whether it will be a basic toolbar or an advanced one.
  614. *
  615. * Note: When you use wysiwyg control use the advanced toolbar, with textarea control use the basic toolbar. Text
  616. * control should not have toolbar.
  617. *
  618. * PHP usage (inside `Widget_Base::render()` method):
  619. *
  620. * $this->add_inline_editing_attributes( 'text', 'advanced' );
  621. * echo '<div ' . $this->get_render_attribute_string( 'text' ) . '>' . $this->get_settings( 'text' ) . '</div>';
  622. *
  623. * @since 1.8.0
  624. * @access protected
  625. *
  626. * @param string $key Element key.
  627. * @param string $toolbar Optional. Toolbar type. Accepted values are `advanced`, `basic` or `none`. Default is
  628. * `basic`.
  629. */
  630. protected function add_inline_editing_attributes( $key, $toolbar = 'basic' ) {
  631. if ( ! Plugin::$instance->editor->is_edit_mode() ) {
  632. return;
  633. }
  634. $this->add_render_attribute( $key, [
  635. 'class' => 'elementor-inline-editing',
  636. 'data-elementor-setting-key' => $key,
  637. ] );
  638. if ( 'basic' !== $toolbar ) {
  639. $this->add_render_attribute( $key, [
  640. 'data-elementor-inline-editing-toolbar' => $toolbar,
  641. ] );
  642. }
  643. }
  644. /**
  645. * Add new skin.
  646. *
  647. * Register new widget skin to allow the user to set custom designs. Must be
  648. * called inside the `_register_skins()` method.
  649. *
  650. * @since 1.0.0
  651. * @access public
  652. *
  653. * @param Skin_Base $skin Skin instance.
  654. */
  655. public function add_skin( Skin_Base $skin ) {
  656. Plugin::$instance->skins_manager->add_skin( $this, $skin );
  657. }
  658. /**
  659. * Get single skin.
  660. *
  661. * Retrieve a single skin based on skin ID, from all the skin assigned to
  662. * the widget. If the skin does not exist or not assigned to the widget,
  663. * return false.
  664. *
  665. * @since 1.0.0
  666. * @access public
  667. *
  668. * @param string $skin_id Skin ID.
  669. *
  670. * @return string|false Single skin, or false.
  671. */
  672. public function get_skin( $skin_id ) {
  673. $skins = $this->get_skins();
  674. if ( isset( $skins[ $skin_id ] ) ) {
  675. return $skins[ $skin_id ];
  676. }
  677. return false;
  678. }
  679. /**
  680. * Get current skin ID.
  681. *
  682. * Retrieve the ID of the current skin.
  683. *
  684. * @since 1.0.0
  685. * @access public
  686. *
  687. * @return string Current skin.
  688. */
  689. public function get_current_skin_id() {
  690. return $this->get_settings( '_skin' );
  691. }
  692. /**
  693. * Get current skin.
  694. *
  695. * Retrieve the current skin, or if non exist return false.
  696. *
  697. * @since 1.0.0
  698. * @access public
  699. *
  700. * @return Skin_Base|false Current skin or false.
  701. */
  702. public function get_current_skin() {
  703. return $this->get_skin( $this->get_current_skin_id() );
  704. }
  705. /**
  706. * Remove widget skin.
  707. *
  708. * Unregister an existing skin and remove it from the widget.
  709. *
  710. * @since 1.0.0
  711. * @access public
  712. *
  713. * @param string $skin_id Skin ID.
  714. *
  715. * @return \WP_Error|true Whether the skin was removed successfully from the widget.
  716. */
  717. public function remove_skin( $skin_id ) {
  718. return Plugin::$instance->skins_manager->remove_skin( $this, $skin_id );
  719. }
  720. /**
  721. * Get widget skins.
  722. *
  723. * Retrieve all the skin assigned to the widget.
  724. *
  725. * @since 1.0.0
  726. * @access public
  727. *
  728. * @return Skin_Base[]
  729. */
  730. public function get_skins() {
  731. return Plugin::$instance->skins_manager->get_skins( $this );
  732. }
  733. }