L’extension Custom Elements pour Contao

par

Lorsque l’on conçoit un projet informatique, il est commun de baser sur ce que l’on appelle des dépendances. On a déjà parlé de Composer la dernière fois, dont l’objectif est de gérer les dépendances d’un projet informatique.

Il y a tellement de choses à penser quand on produit un système qu’il est commun d’utiliser des sous-systèmes, développés par des tiers et mis à disposition de tous.

Ces sous-systèmes remplissent en général un seul objectif, mais ils le font bien, en toute indépendance, et sont régulièrement mis à jour sans que l’on est à s’en inquiéter pendant la vie de notre projet.

Contao, comme Wordpress ou Prestashop, dispose de nombreuses extensions pouvant être utilisées dans la conception d’un site Internet. Aujourd’hui, on va parler de l’une d’entre elles, que l’on utilise fréquemment : Custom Elements, par RockSolid.

A quoi sert Custom Elements ?

Contao met à disposition un grand nombre d’éléments de contenus génériques mais parfois, on a besoin de quelque chose de plus particulier, et c’est là que Custom Elements intervient.

Comme son nom l’indique, Custom Elements permet de créer des éléments de contenus personnalisés et administrables, sans avoir à développer une extension propre à Contao.

Un des avantages de Contao est de pouvoir étendre le backoffice facilement, et de rajouter autant de modules ou d’éléments que nécessaire pour le bon fonctionnement d’un projet web.

Cela demande toutefois une certaine procédure, un peu lourde quand il s’agit de créer un petit élément de contenu ponctuel. D’où la praticité de Custom Elements

Les Custom Elements peuvent contenir tous les champs disponibles pour les éléments natifs à Contao ainsi que vos champs personnalisés, que ce soit des textes, médias, options sous forme de liste déroulante / case à cocher. Il est également possible de rajouter des listes de sous-structures, composés des champs de votre choix.

Blocs de prix
Administration des blocs de prix

Non seulement ils sont administrables via le back-office, mais ils sont aussi aisément modifiables. Vous pouvez rajouter un champ à la volée, sans avoir vraiment de notions de développeurs, et c’est ce que nous allons voir ensuite.

Créer un Custom Element

Pour modifier un Custom Element, vous devez avoir accès à la section “Modèles” de votre back-office, et la possibilité de créer des nouveaux modèles.

Vous devez ensuite créer deux fichiers correspondant à la structure suivante, en remplaçant “element” par un label explicite (pour s’y retrouver ensuite) :

  • rsce_element.html5
  • rsce_element_config.php

Attention à bien mettre ces fichiers à la racine de “Modèles”. Contao ne charge automatiquement que ce qu’il y a dans cette racine, les templates se trouvant dans des dossiers devant être chargés manuellement.

Le fichier config

Globalement, cela fonctionne comme un DCA de Contao. Il faut saisir la configuration, ainsi que les différents champs de backend à afficher. Voici ci-dessous un exemple de l’administration des price cards montrés plus haut.

<?php
$arrColors = array (
                ""=>"Par défaut"
                ,'blue'=> "blue"
                ,'green'=> "green"
                ,'red'=> "red"
                ,'darkblue'=> "darkblue"
                ,'gold'=> "gold"
                ,'black'=> "black"
                ,'blacklight'=> "blacklight"
                ,'blacklighter'=> "blacklighter"
                ,'grey'=> "grey"
                ,'greylight'=> "greylight"
                ,'greylighter'=> "greylighter"
                ,'greystrong'=> "greystrong"
                ,'greystronger'=> "greystronger"
                ,'white'=> "white"
                ,'none'=> "transparent"
            );
            
return array
(
    'label' => array('Price cards', 'Générez une liste de price cards'),
    'types' => array('content'),
    'contentCategory' => 'texts',
    'standardFields' => array('cssID'),
    'fields' => array
    (
        'nbCols_default' => array
        (
            'label' => array('Nombre de colonnes', 'Indiquez le nombre de colonnes souhaité (entre 1 et 12)'),
            'inputType' => 'text',
            'eval' => array('rgxp' => 'digit', 'tl_class' => 'w50 clr', 'min'=>1, 'max'=>12, 'mandatory'=>true)
        ),
        'responsive_legend' => array
        (
            'label' => array('Configuration responsive')
            ,'inputType' => 'group'
        ),
        'nbCols_xl' => array
        (
            'label' => array('Nombre de colonnes < 1400px', 'Indiquez le nombre de colonnes souhaité (entre 1 et 12)'),
            'inputType' => 'text',
            'eval' => array('rgxp' => 'digit', 'tl_class' => 'w50 clr', 'min'=>1, 'max'=>12)
        ),
        'nbCols_lg' => array
        (
            'label' => array('Nombre de colonnes < 1200px', 'Indiquez le nombre de colonnes souhaité (entre 1 et 12)'),
            'inputType' => 'text',
            'eval' => array('rgxp' => 'digit', 'tl_class' => 'w50 clr', 'min'=>1, 'max'=>12)
        ),
        'nbCols_md' => array
        (
            'label' => array('Nombre de colonnes < 992px', 'Indiquez le nombre de colonnes souhaité (entre 1 et 12)'),
            'inputType' => 'text',
            'eval' => array('rgxp' => 'digit', 'tl_class' => 'w50 clr', 'min'=>1, 'max'=>12)
        ),
        'nbCols_sm' => array
        (
            'label' => array('Nombre de colonnes < 768px', 'Indiquez le nombre de colonnes souhaité (entre 1 et 12)'),
            'inputType' => 'text',
            'eval' => array('rgxp' => 'digit', 'tl_class' => 'w50 clr', 'min'=>1, 'max'=>12)
        ),
        'nbCols_xs' => array
        (
            'label' => array('Nombre de colonnes < 620px', 'Indiquez le nombre de colonnes souhaité (entre 1 et 12)'),
            'inputType' => 'text',
            'eval' => array('rgxp' => 'digit', 'tl_class' => 'w50 clr', 'min'=>1, 'max'=>12)
        ),
        'nbCols_xxs' => array
        (
            'label' => array('Nombre de colonnes < 520px', 'Indiquez le nombre de colonnes souhaité (entre 1 et 12)'),
            'inputType' => 'text',
            'eval' => array('rgxp' => 'digit', 'tl_class' => 'w50 clr', 'min'=>1, 'max'=>12)
        ),
        'listItems' => array
        (
            'label' => array('Price Cards', 'Editez les cards')
            ,'elementLabel' => '%s. price card'
            ,'inputType' => 'list'
            ,'fields' => array
            (
                'title' => array
                (
                    'label' => &$GLOBALS['TL_LANG']['tl_content']['headline'],
                    'inputType' => 'text',
                    'eval' => array('mandatory'=>false)
                ),
                'price_legend' => array
                (
                    'label' => array('Prix')
                    ,'inputType' => 'group'
                ),
                'amount' => array
                (
                    'label' => array('Prix', 'Saisissez le prix/nombre')
                    ,'inputType' => 'text'
                    ,'eval' => array('tl_class' => 'clr w50')
                ),
                'currency' => array
                (
                    'label' => array('Devise', 'Saisissez la devise/unité (€,$,£,...)')
                    ,'inputType' => 'text'
                    ,'eval' => array('tl_class' => 'clr w50')
                ),
                'period' => array
                (
                    'label' => array('Récurrence', 'Saisissez la récurrence (par mois, par jour,...)')
                    ,'inputType' => 'text'
                    ,'eval' => array('tl_class' => 'clr w50')
                ),
                'content_legend' => array
                (
                    'label' => array('Contenus')
                    ,'inputType' => 'group'
                ),
                'lines' => array
                (
                    'label' => array('Lignes', 'Ajouter x lignes de contenus (html autorisé)')
                    ,'inputType' => 'listWizard'
                    ,'eval' => array('tl_class' => 'clr w50','allowHtml'=>true)
                ),
                'cta_text' => array
                (
                    'label' => &$GLOBALS['TL_LANG']['tl_content']['linkTitle']
                    ,'inputType' => 'text'
                    ,'eval' => array('tl_class'=>'w50 clr', 'mandatory' => false)
                ),
                'cta_href' => array
                (
                    'label' => &$GLOBALS['TL_LANG']['MSC']['url']
                    ,'inputType' => 'text'
                    ,'eval' => array('rgxp'=>'url', 'tl_class' => 'w50 wizard ')
                    ,'wizard' => array(array('tl_content', 'pagePicker'))
                ),
                'cta_title' => array
                (
                    'label' => &$GLOBALS['TL_LANG']['tl_content']['titleText']
                    ,'inputType' => 'text'
                    ,'eval' => array('tl_class'=>'w50')
                ),
                'cta_target' => array
                (
                    'label' => &$GLOBALS['TL_LANG']['MSC']['target']
                    ,'inputType' => 'checkbox'
                    ,'eval' => array('tl_class'=>'w50')
                ),
                'cta_classes' => array
                (
                    'label' => array('Classes supplémentaires', 'Indiquez, si souhaité, la ou les classes css à ajouter au bouton')
                    ,'inputType' => 'text'
                    ,'eval' => array('tl_class'=>'w50 clr')
                ),
                'style_legend' => array
                (
                    'label' => array('Apparence')
                    ,'inputType' => 'group'
                ),
                'font_color' => array
                (
                    'label' => array('Couleur du texte', 'Si souhaité, ajustez la couleur du contenu'),
                    'inputType' => 'select',
                    'options' => $arrColors,
                    'eval' => array('tl_class'=>'w50'),
                ),
                'bg_color' => array
                (
                    'label' => array('Couleur du fond', 'Si souhaité, ajustez la couleur de fond du bloc'),
                    'inputType' => 'select',
                    'options' => $arrColors,
                    'eval' => array('tl_class'=>'w50'),
                ),
                'content_color' => array
                (
                    'label' => array('Couleur du titre et du prix', 'Si souhaité, ajustez la couleur du titre et du prix'),
                    'inputType' => 'select',
                    'options' => $arrColors,
                    'eval' => array('tl_class'=>'w50'),
                ),
                'isMain' => array
                (
                    'label' => array('Vedette','Met en valeur le bloc')
                    ,'inputType' => 'checkbox'
                    ,'eval' => array('tl_class'=>'w50')
                ),
            )
        ),
    ),
);

Vous pouvez en savoir plus sur le site de RockSolid, la documentation est en allemand, mais se comprend assez bien : https://rocksolidthemes.com/de/contao/plugins/custom-content-elements/dokumentation

Le fichier template

Après le fichier de configuration, bien évidemment, il faut mettre en page tout ça !

Mise à part quelques subtilités, c’est également la même chose qu’un template natif Contao. Vous accédez aux attributs via $this->fieldName. Si vous avez une liste d’éléments, ils seront disponibles sous forme d’Array directement. Toujours l’exemple avec nos priceCards, juste en dessous :

<?php
  $responsiveClasses = '';
  if($this->nbCols_xl)
    $responsiveClasses .= ' cols-xl-'.$this->nbCols_xl;
  if($this->nbCols_lg)
    $responsiveClasses .= ' cols-lg-'.$this->nbCols_lg;
  if($this->nbCols_md)
    $responsiveClasses .= ' cols-md-'.$this->nbCols_md;
  if($this->nbCols_sm)
    $responsiveClasses .= ' cols-sm-'.$this->nbCols_sm;
  if($this->nbCols_xs)
    $responsiveClasses .= ' cols-xs-'.$this->nbCols_xs;
  if($this->nbCols_xxs)
    $responsiveClasses .= ' cols-xxs-'.$this->nbCols_xxs;
?>
<div class="<?= $this->class ?>">
<div class="d-grid cols-<?= $this->nbCols_default ?><?= $responsiveClasses ?>">
  <?php foreach($this->listItems as $item): ?>
    <?php
      $itemClasses = '';
      if ($item->font_color != '')
        $itemClasses.='ft-'.$item->font_color.' ';
      if ($item->bg_color != '')
        $itemClasses.='bg--'.$item->bg_color.' ';
      if ($item->content_color != '')
        $itemClasses.='content--'.$item->content_color.' ';
      if ($item->isMain != '')
        $itemClasses.='main ';
    ?>
    <div class="priceCard <?= $itemClasses ?>">
      <div class="priceCard__wrapper">
        <?php if ($item->title): ?>
          <div class="priceCard__title"><?= $item->title ?></div>
        <?php endif; ?>
        <?php if ($item->amount): ?>
          <div class="priceCard__price">
            <?php if ($item->amount): ?> <span class="priceCard__price__amount"><?= $item->amount ?></span> <?php endif ?>
            <?php if ($item->currency): ?> <span class="priceCard__price__currency"><?= $item->currency ?></span> <?php endif ?>
            <?php if ($item->period): ?> <div class="priceCard__price__period"><?= $item->period ?></div> <?php endif ?>
          </div>
        <?php endif; ?>
        <?php if (count($item->lines) > 0): ?>
          <div class="priceCard__content">
            <?php foreach ($item->lines as $line): ?>
              <div class="priceCard__content__item"><?= $line ?></div>
            <?php endforeach ?>
          </div>
        <?php endif; ?>
        <?php if ($item->cta_href): ?>
          <div class="priceCard__cta">
            <a href="<?= $item->cta_href ?>" title="<?= $item->cta_title ?>" class="btn <?= $item->cta_classes ?>" <?= ($item->cta_target) ? 'target="_blank"':'' ?>><?= $item->cta_text ?></a>
          </div>
        <?php endif; ?>
      </div>
    </div>
  <?php endforeach; ?>
</div>
</div>

Libre à vous ensuite d’appliquer le style et les scripts nécessaires pour le bon fonctionnement de l’élément, directement dedans, ou à un niveau plus global dans Contao.

Le meilleur moyen d’apprendre reste encore de voir ce qui existe et de piquer ce qui nous intéresse. Dans Smartgear, nous avons conçu quelques custom elements pour étendre les possibilités de Contao, vous pouvez les consulter ici : https://github.com/Web-Ex-Machina/contao-smartgear/tree/master/assets/rsce_files

Quelques exemples de Custom Elements

Galerie d'images responsive
Galerie d'images responsive - Administration
Compteurs dynamiques
Compteurs dynamiques - Administration
Blocs responsive
Blocs responsive - Administration

L’extension Custom Elements fait parti des extensions incontournables de Contao. Elle offre la souplesse nécessaire pour créer des éléments personnalisés sans avoir à y passer trop de temps.

Contao reste avant tout un CMS open-source. Il évolue grâce à sa communauté qui fait remonter des suggestions et des erreurs à l’équipe sur leur Github. Chaque développeur peut créer une extension et la diffuser gratuitement, et ainsi bénéficier de retours ou de contributions venant d’autres utilisateurs de Contao.

Il en existe beaucoup d’extensions que l’on aura l’occasion de présenter au cours de prochains articles. Vous pouvez prendre un peu d’avance et jeter un oeil ici : https://packagist.org/?query=contao ou consulter nos extensions ici : https://github.com/Web-Ex-Machina/