Un widget personnalisé pour Elementor

Connaissez-vous le constructeur de page Elementor ?

 

Si non, testez-le vite, il vaut le détour. Je ne vais pas faire ici une présentation détaillée de cet outil, ça a déjà été fait par GeekPress, et bien d’autres.

En bref, Elementor vous permet de construire des pages, avec des sections, des colonnes, et des widgets – tous les « éléments » sont des widgets.

J’avoue que j’ai un peu du mal à me faire à son interface « full front-end », mais mis à part cela le rendu est vraiment bon, et contrairement à certains de ses concurrents, Elementor rend un code plutôt propre, et surtout, il est orienté développeurs.

En effet, je vous invite à parcourir le code qui est très lisible, bien commenté, on se l’approprie facilement, et bien sûr on l’étend !

C’est l’objet de cet article : créer un widget personnalisé pour Elementor afin d’étendre ses fonctionnalités.

Les possibilités sont infinies, on peut faire ce que l’on veut avec un widget, et d’ailleurs il en existe déjà beaucoup dans la version gratuite, et encore plus dans la version payante du constructeur de page.

 

Dans la pratique… un bouton fixe

 

Pour cet article j’ai créé un widget bouton « fixe » : il reprend toutes les fonctionnalités du bouton natif proposé par Elementor, sauf qu’en plus il peut être fixé à droite ou à gauche de l’écran, et restera toujours visible au défilement. Ce sera utile pour un bouton d’appel d’action par exemple.

Ils sont sympas chez Elementor, ils ont créé un « starter plugin », que vous pouvez trouver ici. On peut ainsi comprendre facilement que notre architecture de fichiers dans notre extension se composera ainsi :

 

/elementor-button-fixed
 /assets // tous nos CSS et JS
 /widgets // le dossier contenant notre widget
    - button-fixed.php // la classe du widget
 - elementor-button-fixed.php // chargement du plugin
 - plugin.php // la classe principale

(Gardez en tête que la grande majorité des fonctions utilisées ci-dessous, dans les extraits de codes, sont natives à Elementor, pour plus de détails sur celles-ci je vous invite à faire quelques recherches dans son code source).

Commençons par le fichier elementor-button-fixed.php qui charge l’extension :

<?php

/**
 * Plugin Name: Elementor Button Fixed
 * Description: Fixed button for Elementor page builder
 * Plugin URI: https://github.com/MarieComet/Elementor-Button-Fixed/
 * Version: 0.0.1
 * Author: Marie Comet
 * Author URI: https://mariecomet.fr
 * Text Domain: elementor-button-fixed
 */

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

define( 'ELEMENTOR_MC_CUSTOM__FILE__', __FILE__ );

/**
 * Load Hello World
 *
 * Load the plugin after Elementor (and other plugins) are loaded.
 *
 * @since 1.0.0
 */
function mc_custom_widgets_load() {
    // Load localization file
    load_plugin_textdomain( 'mc-custom-widgets' );

    // Notice if the Elementor is not active
    if ( ! did_action( 'elementor/loaded' ) ) {
        add_action( 'admin_notices', 'mc_custom_widgets_fail_load' );
        return;
    }

    // Check version required
    $elementor_version_required = '1.0.0';
    if ( ! version_compare( ELEMENTOR_VERSION, $elementor_version_required, '>=' ) ) {
        add_action( 'admin_notices', 'mc_custom_widgets_fail_load_out_of_date' );
        return;
    }

    // Require the main plugin file
    require( __DIR__ . '/plugin.php' );
}
add_action( 'plugins_loaded', 'mc_custom_widgets_load' );

Rien de particulier à signaler ici, on déclare notre extension via l’entête, on fait quelques vérifications sur la présence d’Elementor et sa version, notez qu’en ligne 42 on charge le fichier plugin.php, le tout hooké sur `plugins_loaded`.

 


 

Passons au fichier suivant : plugin.php dans lequel on va déclarer notre classe principale et appeler notre classe propre de notre widget :

<?php
namespace MCCustomWidget;

use MCCustomWidget\Widgets\MC_Button_Fixed;

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

/**
 * Main Plugin Class
 *
 * Register new elementor widget.
 *
 * @since 1.0.0
 */
class Plugin {

    /**
     * Constructor
     *
     * @since 1.0.0
     *
     * @access public
     */
    public function __construct() {
        $this->add_actions();
    }

    /**
     * Add Actions
     *
     * @since 1.0.0
     *
     * @access private
     */
    private function add_actions() {
        add_action( 'elementor/widgets/widgets_registered', [ $this, 'on_widgets_registered' ] );

        add_action( 'elementor/frontend/after_register_styles', function() {
            wp_register_style( 'button-fixed-css', plugins_url( '/assets/css/button-fixed-css.css', ELEMENTOR_MC_CUSTOM__FILE__ ) );
        } );

        add_action( 'elementor/frontend/after_enqueue_styles', function() {
            wp_enqueue_style( 'button-fixed-css' );
        } );
    }

    /**
     * On Widgets Registered
     *
     * @since 1.0.0
     *
     * @access public
     */
    public function on_widgets_registered() {
        $this->includes();
        $this->register_widget();
    }

    /**
     * Includes
     *
     * @since 1.0.0
     *
     * @access private
     */
    private function includes() {
        require __DIR__ . '/widgets/button-fixed.php';
    }

    /**
     * Register Widget
     *
     * @since 1.0.0
     *
     * @access private
     */
    private function register_widget() {
        \Elementor\Plugin::instance()->widgets_manager->register_widget_type( new MC_Button_Fixed() );
    }
}

new Plugin();

Vous remarquerez que j’utilise des espaces de noms (lignes 2 et 4), si besoin vous pouvez lire cet excellent tuto sur Open Class Room.

Décortiquons la classe Plugin :

  • – La fonction add_actions sert à charger notre fichier CSS, qui selon votre projet ne sera pas indispensable. Cette fonction est appelée dans la fonction __construct.

– La fonction includes, comme son nom l’indique, inclue le fichier de notre widget : /widgets/button-fixed.php

  • – La fonction register_widget indique à Elementor qu’il doit ajouter notre widget aux siens.
  • – Ces deux dernières fonctions sont lancées dans la fonction on_widgets_registered qui elle même est hookée sur elementor/widgets/widgets_registered.

 

Maintenant que nous avons indiqué à Elementor qu’on a un nouveau widget pour lui, il faut le créer, passons au fichier /widgets/button-fixed.php :

<?php
namespace MCCustomWidget\Widgets;

use Elementor\Widget_Base;
use Elementor\Controls_Manager;
use Elementor\Group_Control_Typography;
use Elementor\Scheme_Typography;
use Elementor\Scheme_Color;
use Elementor\Group_Control_Border;
use Elementor\Group_Control_Box_Shadow;


if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

/**
 * Elementor Button fixed
 *
 * Elementor widget for fixed button.
 *
 * @since 1.0.0
 */
class MC_Button_Fixed extends Widget_Base {

    /**
     * Retrieve button widget name.
     *
     * @access public
     *
     * @return string Widget name.
     */
    public function get_name() {
        return 'button-fixed';
    }

    /**
     * Retrieve button widget title.
     *
     * @access public
     *
     * @return string Widget title.
     */
    public function get_title() {
        return __( 'Button Fixed', 'elementor' );
    }

    /**
     * Retrieve button widget icon.
     *
     * @access public
     *
     * @return string Widget icon.
     */
    public function get_icon() {
        return 'eicon-button';
    }

Tout d’abord remarquez la déclaration de mon espace de nom, et également l’appel aux espaces de noms d’Elementor (use). Ils sont importants car j’indique que je vais les utiliser, sans cela nous aurions des erreurs fatales.

En ligne 22 nous lançons notre classe qui étend la classe Widget_Base (elementor/includes/base/widget-base.php).

En ligne 31 la fonction get_name sert à enregistrer notre « identifiant » de widget.

En ligne 42 la fonction get_title sert à enregistrer notre « titre » de widget, c’est celui qui sera visible en back-office.

En ligne 53 la fonction get_icon sert à indiquer quel icône nous souhaitons utiliser pour notre widget.

Plus complexe, la fonction _register_controls sert à déclarer toutes les sections, paramètres et options que nous souhaitons rendre disponibles pour notre widget :

    protected function _register_controls() {
        $this->start_controls_section(
            'section_button',
            [
                'label' => __( 'Button Fixed', 'elementor' ),
            ]
        );

        $this->add_control(
            'button_type',
            [
                'label' => __( 'Type', 'elementor' ),
                'type' => Controls_Manager::SELECT,
                'default' => '',
                'options' => [
                    '' => __( 'Default', 'elementor' ),
                    'info' => __( 'Info', 'elementor' ),
                    'success' => __( 'Success', 'elementor' ),
                    'warning' => __( 'Warning', 'elementor' ),
                    'danger' => __( 'Danger', 'elementor' ),
                ],
                'prefix_class' => 'elementor-button-',
            ]
        );

        $this->add_control(
            'text',
            [
                'label' => __( 'Text', 'elementor' ),
                'type' => Controls_Manager::TEXT,
                'default' => __( 'Click me', 'elementor' ),
                'placeholder' => __( 'Click me', 'elementor' ),
            ]
        );

        $this->add_control(
            'link',
            [
                'label' => __( 'Link', 'elementor' ),
                'type' => Controls_Manager::URL,
                'placeholder' => 'http://your-link.com',
                'default' => [
                    'url' => '#',
                ],
            ]
        );

        $this->add_responsive_control(
            'align_fixed',
            [
                'label' => __( 'Alignment', 'elementor' ),
                'type' => Controls_Manager::CHOOSE,
                'options' => [
                    'left_fixed'    => [
                        'title' => __( 'Left', 'elementor' ),
                        'icon' => 'fa fa-align-left',
                    ],
                    'right_fixed' => [
                        'title' => __( 'Right', 'elementor' ),
                        'icon' => 'fa fa-align-right',
                    ],
                ],
                'prefix_class' => 'elementor%s-align-fixed',
                'default' => 'left_fixed',
            ]
        );

Plusieurs fonctions nous seront utiles :

  • start_controls_section ouvre une nouvelle section (ce sont les onglets en haut dans le panneau d’Elementor lorsqu’on édite un widget)
  • add_control créé un nouveau contrôle, c’est à dire une option, par exemple en ligne 90 un sélecteur qui permet de choisir le type de notre bouton. Je vous invite à parcourir les widgets natifs d’Elementor pour tous les découvrir.
  • add_responsive_control fait la même chose que add_control, avec une gestion du responsive en plus. En ligne 129 je défini que je veux un choix « droite » ou « gauche » pour l’alignement de mon bouton.
  • – end_controls_section sert à fermer la section que l’on a ouverte.

 

Pour le rendu en back-office, lors de l’édition d’une page, c’est la fonction _content_template qui se charge de l’affichage, et on utilise une variable JavaScript ‘settings‘ :

    protected function _content_template() {
        ?>
        <div class="elementor-button-wrapper button-fixed-wrapper {{ settings.align_fixed }}">
            <a class="elementor-button elementor-size-{{ settings.size }} elementor-animation-{{ settings.hover_animation }}" href="{{ settings.link.url }}">
                <span class="elementor-button-content-wrapper">
                    <# if ( settings.icon ) { #>
                    <span class="elementor-button-icon elementor-align-icon-{{ settings.icon_align }}">
                        <i class="{{ settings.icon }}"></i>
                    </span>
                    <# } #>
                    <span class="elementor-button-text">{{{ settings.text }}}</span>
                </span>
            </a>
        </div>
        <?php
    }

 


 

Nous avons donc définis les paramètres qui seront rendus en administration, place à la partie front-office, toujours dans le même fichier :

    protected function render() {
        $settings = $this->get_settings();

        $this->add_render_attribute( 'wrapper', 'class', 'elementor-button-wrapper '.  $settings['align_fixed']);

        if ( ! empty( $settings['link']['url'] ) ) {
            $this->add_render_attribute( 'button', 'href', $settings['link']['url'] );
            $this->add_render_attribute( 'button', 'class', 'elementor-button-link' );

            if ( $settings['link']['is_external'] ) {
                $this->add_render_attribute( 'button', 'target', '_blank' );
            }

            if ( $settings['link']['nofollow'] ) {
                $this->add_render_attribute( 'button', 'rel', 'nofollow' );
            }
        }

        $this->add_render_attribute( 'button', 'class', 'elementor-button' );

        if ( ! empty( $settings['size'] ) ) {
            $this->add_render_attribute( 'button', 'class', 'elementor-size-' . $settings['size'] );
        }

        if ( $settings['hover_animation'] ) {
            $this->add_render_attribute( 'button', 'class', 'elementor-animation-' . $settings['hover_animation'] );
        }

        if ( $settings['align_fixed'] ) {
            $this->add_render_attribute( 'button', 'class', 'elementor-fixed-pos-' . $settings['align_fixed'] );
        }

        ?>
        <div <?php echo $this->get_render_attribute_string( 'wrapper' ); ?>>
            <a <?php echo $this->get_render_attribute_string( 'button' ); ?>>
                <?php $this->render_text(); ?>
            </a>
        </div>
        <?php
    }

La fonction render contrôle l’affichage du « markup » (html) de notre widget en front-office :

  • get_settings sert à récupérer tous nos paramètres précédemment déclarés dans l’administration.
  • add_render_attribute ajoute une valeur à un attribut de l’élément : le premier paramètre est l’élément à cibler, le deuxième son attribut (class, ID, etc), le troisième la valeur à ajouter.
  • get_render_attributes sert à récupérer la valeur d’un attribut, elle prend en paramètre le nom de cet attribut.
  • render_text est l’appel à la fonction qui affiche le contenu (ligne 452), je ne la détaillerai pas ici car le fonctionnement est sensiblement le même que render.

 

Voilà ! Vous pouvez retrouver l’extension sur mon compte Github.

Bouton fixé
Bouton fixé dans le constructeur de page

 

Vous êtes maintenant prêt à créer vos widgets personnalisés directement dans Elementor.

 

Crédit photo : pixel2013 / Pixabay

Vous avez des questions ?
Contactez-moi :
Me contacter

13 thoughts on “Un widget personnalisé pour Elementor

  1. Bonjour,

    article très intéressant.
    Peut-être pouvez-vous m’aider en tant que pro d’elementor.

    Est-il possible d’ajouter un method= »POST » dans les formulaires générés par elementor (pro)

    Merci
    Jeanson

    1. Bonjour Jeanson,
      Désolée mais je ne sais pas si c’est possible.
      Vous pouvez poser la question au support d’Elementor, ils auront surement la réponse.

      1. Bonjour Marie,

        merci pour votre réponse.
        En fait, je me suis rendu compte que le form est déjà en method POST. Du coup, ma question est caduque.
        Cependant, je ne comprends pas pourquoi lorsque je fais un traitement_formulaire avec add_action(‘template_redirect’, ‘traitement_formulaire’); et que je fais un print_r($_POST) ; j’ai un Array() vide.
        Si jamais vous avez une idée…

        Jeanson

        1. Bonjour Jeanson,
          Ce retour vide peut être causé par énormément de facteurs, c’est difficile de vous aider avec seulement ces informations.

  2. Hello,

    je voulais vous remercier pour votre tuto qui est vraiment super bien fait.

    C’est vrai que c’est super de pouvoir ajouter des plugins, mais est-ce que vous savez comment si prendre pour faire des skins pour les widgets déjà existants ?

    J’ai réussi à créer des skins, mais le seul problème, c’est que je ne suis pas capable d’en faire des plugins. Ce qui m’oblige à modifier directement le plugin d’Elementor et ce n’est vraiment pas propre.

    J’ai essayé de mélanger votre tuto et ce que la doc propose, mais je n’ai pas réussi.

    Est-ce que vous avez une idée ?

    1. Bonjour Éric,
      Merci pour votre commentaire !
      Avez vous lu cette partie de la documentation https://github.com/pojome/elementor/blob/master/docs/content/hooks/php-hooks.md#elementorwidgetnameskins_init ?
      Elle donne le bon hook pour ajouter un skin à un widget existant, et dans l’exemple ‘Skin_Dark_Map’ serait, dans votre cas, le nom de votre classe dans laquelle vous instanciez votre code.
      Que voulez vous dire par « je n’arrive pas à un faire un plugin »?
      Qu’est ce qui bloque?

      1. Bonjour,

        Oui j’ai fait un tour sur la documentation. J’ai justement utilisé cette façon pour ajouter un skin, mais je n’ai pas réussi à l’utiliser comme dans la doc.
        Je n’ai pas réussi à adapter le bricolage que j’ai fait pour en faire un plugin, c’est cela que je voulais dire. J’aimerais avoir un code propre sans avoir à modifier le code source d’Elementor ^^. Histoire de pouvoir faire les mises à jour sans problème.

        Je pense que je ne suis pas très loin de réussir à faire fonctionner mon code, mais pour le moment ce n’est pas le cas.

        Voici mon github, peut-être que vous arrivez à voir mon problème : https://github.com/tungdil213/Add-skin-elementor

        Pour le moment j’ai cette erreur :
        Fatal error: Uncaught Symfony\Component\Debug\Exception\FatalThrowableError: Class ‘CS_Posts\Skins\Skin_Custom’ not found in wp-content/plugins/elementor-add/plugin.php:15 Stack trace: #0
        wp-includes/class-wp-hook.php(298): CS_Posts\Plugin->CS_Posts\{closure}(Object(ElementorPro\Modules\Posts\Widgets\Posts)) #1
        wp-includes/class-wp-hook.php(323): WP_Hook->apply_filters( », Array) #2
        wp-includes/plugin.php(453): WP_Hook->do_action(Array) #3
        wp-content/plugins/elementor/includes/base/widget-base.php(135): do_action(‘elementor/widge…’, Object(ElementorPro\Modules\Posts\Widgets\Posts)) #4
        /home/clients/7bd44129349568a157b53f736d417af2/web/dev_structo- in wp-content/plugins/elementor-add/plugin.php on line 15

        1. Bonjour Eric,
          J’ai forké votre repo ici : https://github.com/MarieComet/Add-skin-elementor pour corriger votre code.
          Vous pouvez voir les modifications effectuées ici : https://github.com/MarieComet/Add-skin-elementor/commit/6f53c96a0e940618dc9198b16d6040c1470c8b5a
          Je vous laisse regarder les changements, en résumé voici les principaux problèmes corrigés :
          – Vous n’étiez pas hooké sur la bonne action (elementor/widgets/widgets_registered)
          – Vous n’aviez pas inclus le fichier de votre skin (function includes)
          – Dans votre fichier de skin vous ne faisiez pas appel à « use Elementor\Skin_Base; », du coup le « class Skin_Custom extends Skin_Base » ne pouvais pas fonctionner
          – Vous devez utiliser les même fonctions que la classe parente (Skin_Base) (render, etc).

          Avec cette base la skin personnalisée apparait bien dans les widgets « posts », vous pouvez maintenant la construire réellement.
          Bon développement ! 🙂

          1. Bonjour,

            Merci beaucoup pour votre aide très précieuse. Je suis parvenu à tout faire fonctionner, j’ai maintenant un plugin qui permet d’ajouter des templates aux widgets D’Elementor.

            Je comprends mes erreurs, je manque de pratique en langage objet, et je fais parfois ce genre d’erreur.

            Pour ce qui est d’utiliser les mêmes fonctions que la classe parente, c’est effectivement, car j’utilise la classe « Skin_Base » d’Elementor pro, du coup il y a des variations.

            Encore merci pour le temps que vous avez consacré à mon problème.

            Bonne continuation.

            Monnier Eric

  3. Merci pour ce tuto, ça peux servir !!
    Juste une question, je vois que tu utilise Pastacode pour partager du code source, j’ai télécharger le plugin mais il ne semble pas compatible avec elementor, il y a pas de widget WordPress Pastacode dans les widgets de Elementor, du coup je ne peux pas utiliser Pastacode.

    As-tu une idée pour le rendre compatible ?

    1. Oui j’utilise bien Pastacode, mais pas Elementor 🙂
      Est-ce qu’en insérant un widget « éditeur de texte » avec Elementor tu n’aurais pas l’outil d’insertion Pastacode dans la barre d’outil WYSIWYG ?

  4. Non justement il y a pas de possiblité d’ajouter Pastacode dans l’éditeur de texte Elementor.

    J’ai trouvé un autre moyen, il faut passer par l’éditeur de texte de WordPress, mais pas pratique et pas très fonctionnel, l’idéal reste un widget directement dans elementor ou ajouter pastacode dans l’outil WYSIWYG, mais pas très simple pour moi.

    ps: (on ne reçoit pas de notification par email quand une autre réponse est apportée sur ton blog, pas pratique)

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nom *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

fringilla felis eleifend id adipiscing risus. Sed ipsum