use Elementor\Core\Utils\Promotions\Filtered_Promotions_Manager;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
* An abstract class to register new Elementor widgets. It extended the
* `Element_Base` class to inherit its properties.
* This abstract class must be extended in order to register new widgets.
abstract class Widget_Base extends Element_Base {
* Whether the widget has content.
* Used in cases where the widget has no content. When widgets uses only
* skins to display dynamic content generated on the server. For example the
* posts widget in Elementor Pro. Default is true, the widget has content
protected $_has_template_content = true;
private $is_first_section = true;
* Registered Runtime Widgets.
* Registering in runtime all widgets that are being used on the page.
public static $registered_runtime_widgets = [];
* Retrieve the element type, in this case `widget`.
* @return string The type.
public static function get_type() {
* Retrieve the widget icon.
* @return string Widget icon.
public function get_icon() {
* Retrieve the widget keywords.
* @return array Widget keywords.
public function get_keywords() {
* Retrieve the widget categories.
* @return array Widget categories.
public function get_categories() {
* Get widget upsale data.
* Retrieve the widget promotion data.
* @return array|null Widget promotion data.
protected function get_upsale_data() {
* Widget base constructor.
* Initializing the widget base class.
* @throws \Exception If arguments are missing when initializing a full widget
* @param array $data Widget data. Default is an empty array.
* @param array|null $args Optional. Widget default arguments. Default is null.
public function __construct( $data = [], $args = null ) {
parent::__construct( $data, $args );
$is_type_instance = $this->is_type_instance();
if ( ! $is_type_instance && null === $args ) {
throw new \Exception( 'An `$args` argument is required when initializing a full widget instance.' );
if ( $is_type_instance ) {
if ( $this->has_own_method( '_register_skins', self::class ) ) {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( '_register_skins', '3.1.0', __CLASS__ . '::register_skins()' );
$this->_register_skins();
$widget_name = $this->get_name();
* Fires when Elementor widget is being initialized.
* The dynamic portion of the hook name, `$widget_name`, refers to the widget name.
* @param Widget_Base $this The current widget.
do_action( "elementor/widget/{$widget_name}/skins_init", $this );
* Retrieve the widget stack of controls.
* @param bool $with_common_controls Optional. Whether to include the common controls. Default is true.
* @return array Widget stack of controls.
public function get_stack( $with_common_controls = true ) {
$stack = parent::get_stack();
if ( $with_common_controls && ! $this instanceof Widget_Common_Base ) {
/** @var Widget_Common_Base $common_widget */
$common_widget = Plugin::$instance->widgets_manager->get_widget_types( $this->get_common_widget_name() );
$stack['controls'] = array_merge( $stack['controls'], $common_widget->get_controls() );
$stack['tabs'] = array_merge( $stack['tabs'], $common_widget->get_tabs_controls() );
private function get_common_widget_name() {
if ( Plugin::$instance->experiments->is_feature_active( 'e_optimized_markup' ) ) {
return $this->has_widget_inner_wrapper() ? 'common' : 'common-optimized';
* Get widget controls pointer index.
* Retrieve widget pointer index where the next control should be added.
* While using injection point, it will return the injection point index. Otherwise index of the last control of the
* current widget itself without the common controls, plus one.
* @return int Widget controls pointer index.
public function get_pointer_index() {
$injection_point = $this->get_injection_point();
if ( null !== $injection_point ) {
return $injection_point['index'];
return count( $this->get_stack( false )['controls'] );
* Whether to show the widget in the panel or not. By default returns true.
* @return bool Whether to show the widget in the panel or not.
public function show_in_panel() {
* Whether to hide the widget on search in the panel or not. By default returns false.
* @return bool Whether to hide the widget when searching for widget or not.
public function hide_on_search() {
* Start widget controls section.
* Used to add a new section of controls to the widget. Regular controls and
* Note that when you add new controls to widgets they must be wrapped by
* `start_controls_section()` and `end_controls_section()`.
* @param string $section_id Section ID.
* @param array $args Section arguments Optional.
public function start_controls_section( $section_id, array $args = [] ) {
parent::start_controls_section( $section_id, $args );
if ( $this->is_first_section ) {
$this->register_skin_control();
$this->is_first_section = false;
* Register the Skin Control if the widget has skins.
* An internal method that is used to add a skin control to the widget.
* Added at the top of the controls section.
private function register_skin_control() {
$skins = $this->get_skins();
if ( ! empty( $skins ) ) {
if ( $this->_has_template_content ) {
$skin_options[''] = esc_html__( 'Default', 'elementor' );
foreach ( $skins as $skin_id => $skin ) {
$skin_options[ $skin_id ] = $skin->get_title();
// Get the first item for default value.
$default_value = array_keys( $skin_options );
$default_value = array_shift( $default_value );
if ( 1 >= count( $skin_options ) ) {
'label' => esc_html__( 'Skin', 'elementor' ),
'type' => Controls_Manager::HIDDEN,
'default' => $default_value,
'label' => esc_html__( 'Skin', 'elementor' ),
'type' => Controls_Manager::SELECT,
'default' => $default_value,
'options' => $skin_options,
* Register widget skins - deprecated prefixed method
* @deprecated 3.1.0 Use `register_skins()` method instead.
protected function _register_skins() {
Plugin::$instance->modules_manager->get_modules( 'dev-tools' )->deprecation->deprecated_function( __METHOD__, '3.1.0', 'register_skins()' );
* This method is activated while initializing the widget base class. It is
* used to assign skins to widgets with `add_skin()` method.
* protected function register_skins() {
* $this->add_skin( new Skin_Classic( $this ) );
protected function register_skins() {}
* Retrieve the current widget initial configuration.
* Adds more configuration on top of the controls list, the tabs assigned to
* the control, element name, type, icon and more. This method also adds
* widget type, keywords and categories.
* @return array The initial widget config.
protected function get_initial_config() {
'widget_type' => $this->get_name(),
'keywords' => $this->get_keywords(),
'categories' => $this->get_categories(),
'html_wrapper_class' => $this->get_html_wrapper_class(),
'show_in_panel' => $this->show_in_panel(),
'hide_on_search' => $this->hide_on_search(),
'upsale_data' => $this->get_upsale_data(),
'is_dynamic_content' => $this->is_dynamic_content(),
'has_widget_inner_wrapper' => $this->has_widget_inner_wrapper(),
if ( isset( $config['upsale_data'] ) && is_array( $config['upsale_data'] ) ) {
$filter_name = 'elementor/widgets/' . $this->get_name() . '/custom_promotion';
$config['upsale_data'] = Filtered_Promotions_Manager::get_filtered_promotion_data( $config['upsale_data'], $filter_name, 'upgrade_url' );
if ( isset( $config['upsale_data']['image'] ) ) {
$config['upsale_data']['image'] = esc_url( $config['upsale_data']['image'] );
$stack = Plugin::$instance->controls_manager->get_element_stack( $this );
$config['controls'] = $this->get_stack( false )['controls'];
$config['tabs_controls'] = $this->get_tabs_controls();
return array_replace_recursive( parent::get_initial_config(), $config );
protected function should_print_empty() {
* Print widget content template.
* Used to generate the widget content template on the editor, using a
* Backbone JavaScript template.
* @param string $template_content Template content.
protected function print_template_content( $template_content ) {
if ( $this->has_widget_inner_wrapper() ) : ?>
<div class="elementor-widget-container">
Utils::print_unescaped_internal_string( $template_content );
if ( $this->has_widget_inner_wrapper() ) : ?>
* Parses the content from rich text editor with shortcodes, oEmbed and
* @param string $content Text editor content.
* @return string Parsed content.
protected function parse_text_editor( $content ) {
/** This filter is documented in wp-includes/widgets/class-wp-widget-text.php */
$content = apply_filters( 'widget_text', $content, $this->get_settings() );
$content = shortcode_unautop( $content );
$content = do_shortcode( $content );
$content = wptexturize( $content );
if ( $GLOBALS['wp_embed'] instanceof \WP_Embed ) {
$content = $GLOBALS['wp_embed']->autoembed( $content );
* Safe print parsed text editor.
* @uses static::parse_text_editor.
* @param string $content Text editor content.
final protected function print_text_editor( $content ) {
// PHPCS - the method `parse_text_editor` is safe.
echo static::parse_text_editor( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
* Get HTML wrapper class.
* Retrieve the widget container class. Can be used to override the
* container class for specific widgets.
protected function get_html_wrapper_class() {
return 'elementor-widget-' . $this->get_name();
* Add widget render attributes.
* Used to add attributes to the current widget wrapper HTML tag.
protected function add_render_attributes() {
parent::add_render_attributes();