Planète

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Webform - avoir un destinataire dynamique en fonction d'une catégorie

Dans le contexte d'un webform, je voulais pouvoir envoyer une notification à un email en particulier en fonction du sujet du message de contact.

Au niveau de mon webform, mon champ sujet est un select dont les options sont issues d'un vocabulaire de taxonomie :

Image

Webform sujet taxonomy

 Dans le vocabulaire en question, j'ai ajouté un champ de type email field_destinataire_message :

Image

Taxo field email

Ainsi pour chaque sujet de contact, l'administrateur pourra saisir un destinataire différent.

Maintenant reste la partie la plus importante, le gestionnaire de notification du webform.

Création d'un plugin EmailWebformHandler :

web/modules/custom/mon_module/src/Plugin/WebformHandler/ContactWebformHandler.php

get('mail');     // Je recupère la valeur du champ « sujet » (un entier : l'id du terme de taxonomy)    $subjectTid = $webform_submission->getRawData()['subject'];    if (($subject = Term::load($subjectTid)) && $destinataire = $subject->get('field_destinataire_message')->value) {      $recipient = $destinataire;    }    // je définis le destinataire    $message['to_mail'] = $recipient;     parent::sendMessage($webform_submission, $message);  } }

Pour terminer, il faut activer cette notification cela se passe au niveau de notre webform dans l'administration : onglet « paramètres », sous-onglet « Emails / Handlers » : Ajouter un gestionnaire :

Image

webform handler

Sélectionner le handler que l'on vient de créer puis cliquez sur le lien « Ajouter un gestionnaire ».

Et il ne restera plus qu'à configurer la notification comme classiquement, sauf qu'évidement cette fois le champ destinataire n'est pas disponible !

webform handler destinataire

Par kgaut
Kevin Gautreau

Vidéo mini formation : Versionner son projet Drupal avec Git

Une nouvelle vidéo fraîchement mise en ligne pour le weekend. Au programme du jour : Git et comment l'utiliser sur un projet drupal.

Mini introduction sur git et ses fondamentaux
Passage en revue des dossiers et fichiers composants l'architecture d'un projet Drupal
Exemple d'organisation pour travailler en équipe avec les branches Git et les environnements.

Attention, je le répète, dans la troisième parti je présente une organisation de fonctionnement qui me convient à moi et qui répond aux problématiques que j'ai rencontré. Mais ça n'est pas la seule et unique manière de faire et encore moins la meilleure.

Désolé pour le petit faux-contact au niveau du micro qui entraîne parfois un peu de bruit, mon ingé son est viré, le problème est résolu.

Le dépôt utilisé dans la vidéo est librement accessible : https://gitlab.kgaut.net/kgaut/formation-drupal-git

Retrouvez la vidéo sur youtube directement (pensez à passer en plein écran) ou bien juste ci-dessous.

N'oubliez-pas que vous pouvez voter ou me proposer des sujets à l'adresse suivante : https://kgaut.net/suggestion-sujet-video-formation.

Commandes GIT vues dans la vidéo

Initialiser un dépôt

git init

Versionner un fichier et le commiter

git add readme.mdgit commit -m "Ajout fichier readme"

Ajouter une remote

git remote add origin URL_DEPOT_GIT

Créer un tag git

git tag NOMTAG # exemple :  git tag 1.0.0 # ou  git tag 20201030-01

Créer une nouvelle branche git

git checkout -b feature-blog

 

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Installer un nouveau type d'entité via un HOOK_update

Voici comment installer un type d'entité personnalisé via un hook_update.

À noter, les types d'entités d'un module sont automatiquement installés lors de l'installation du module. Ce snippet n'est utile que pour un type d'entité créé à postériori.

/** * Create entity Type « inscription_newsletter » */function MON_MODULE_update_8008() {  \Drupal::entityTypeManager()->clearCachedDefinitions();  \Drupal::entityDefinitionUpdateManager()->installEntityType(\Drupal::entityTypeManager()->getDefinition('inscription_newsletter'));}

 

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Entité - Champ de base « email »

Voici comment définir une propriété (basefield) « e-mail » sur un type d'entité.

    $fields['email'] = BaseFieldDefinition::create('email')      ->setLabel(t('Email'))      ->setDefaultValue('')      ->setDisplayConfigurable('form', TRUE)      ->setDisplayConfigurable('view', TRUE);

 

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Afficher un formulaire dans un bloc ou un contrôleur

Il est possible dans un contrôleur ou un bloc de récupérer un formulaire et de l'afficher comme n'importe quelle autre variable.

À l'époque de drupal 7 on utilisait la fonction drupal_get_form(), à partir de drupal 8, il faut utiliser le service form_builder et sa méthode getForm() en lui passant la classe du formulaire :

#dans la méthode build de mon bloc ou mon controleur :$build['#mon_formulaire'] = \Drupal::service('form_builder')->getForm(\Drupal\mon_module\Form\LoginForm::class);$build['#theme'] = 'mon_template';

Note : il est toujours préférable d'injecter le service en utilisant l'injection de dépendance.

Ensuite il sera possible d'afficher le formulaire dans le template via la variable mon_formulaire :

{# Dans le template twig : mon-template.html.twig #}{{ mon_formulaire }}

Évidement, il ne faut pas oublier d'avoir déclaré la variable mon_formulaire dans la déclaration du template :

//mon_module.module  function mon_module_theme() {  $themes = [];  $themes['mon_template'] = [    'render element' => 'elements',    'variables' => [      'mon_formulaire' => [],    ],    'template' => 'mon-template',  ];   return $themes;}

 

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Entité - Champ de base « Référence à un media »

 Voila comment ajouter une propriétée faisant référence à un média sur un type d'entité :

$fields['header_image'] = BaseFieldDefinition::create('entity_reference')  ->setLabel(t('Header image'))  ->setSetting('target_type', 'media')  ->setSetting('handler_settings', ['target_bundles' => ['image' => 'image']])  ->setDisplayConfigurable('form', TRUE)  ->setDisplayConfigurable('view', TRUE);

Via la ligne ci-dessous, il est possible de spécifier les types de media acceptés

  ->setSetting('handler_settings', ['target_bundles' => ['image' => 'image']])

 

Par kgaut
Kevin Gautreau

Vidéo mini formation : La configuration dans Drupal et le module config_split

Deuxième épisode de mes vidéos de mini-formations à Drupal avec au sujet du jour un point important : la gestion de la configuration dans Drupal.

Qu'est-ce que la configuration ? Comment l'exporter, l'importer, mais aussi et surtout comment, à l'aide du module config_split.

Retrouvez la vidéo sur youtube à l'adresse suivante : https://youtu.be/qy_nq3_sbyM

ou bien directement ci-dessous :

 

Pour proposer des sujets, c'est par ici.

Quelques indications / précisions.

Drush

Installé dans vendor/bin/drush, il peut être appelé directement via se chemin

# Depuis la racine de votre projetvendor/bin/drush COMMANDE

Il est aussi possible d'installer drush-launcher pour éxecuter directement la commande drush : https://github.com/drush-ops/drush-launcher

Activer manuellement un environnement de config split

Dans votre fichier settings.local.php :

// « dev » correspond au nom machine de l'environnement à activer$config['config_split.config_split.dev']['status'] = TRUE;

Les spécificités de l'environnement « prod »

La configuration spécifique de production est exportée dans un dossier ../config/prod

Via un .gitignore, j'exclue du versioning le contenu de ce dossier, afin que si la configuration est modifiée en prod, son export ne gène pas GIT, qu'il ne détecte pas de fichiers modifié.

Process d'un déploiement en production

Qu'il soit automatisé ou non, pour rappel, cette solution me convient personnellement, mais elle ne répond pas forcément à toutes les problématiques, n'hésitez-pas à l'adapter si besoin.

Connexion en SSH au serveur
Script vérifiant qu'aucun fichier n'a été modifié via un git status, si des fichiers sont modifiés alors un mail m'est envoyé et le déploiement est annulé
Script vérifiant que la configuration active n'a pas été modifiée directement en prod. Pour cela un config export est fait, si des modifications sont détectées (encore via un git status) alors un mail m'est envoyé et le déploiement est annulé. La configuration spécifique à l'environnement étant exportée dans un dossier exclu du versioning, alors aucune modification n'est détectée.
git pull (ou )
composer install --no-dev pour insta
drush updb (pour lancer les éventuelles mise à jour de la base de données)
drush config:import afin d'importer la configuration éventuellement modifiée, (la config spécifique de prod ayant été exportée à l'étape 3, elle sera réimportée à ce moment là.

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Views - Utiliser un view_mode spécifique pour un seul élément

Dans la continuité de Drupal 8 & Drupal 9 - Views - créer un Pager personnalisé.

Sur un de mes projets j'ai une vue qui liste des nœuds de type « Article » avec le mode d'affichage (view_mode) « Teaser », mais j'avais besoin d'afficher le premier élément de la première avec un autre mode d'affichage, le contenu prendra toute la largeur, au lieu d'un tiers pour les suivants.

J'ai pour cela utilisé le hook HOOK_views_post_render :

function MON_MODULE_views_post_render(ViewExecutable $view, &$output, \Drupal\views\Plugin\views\cache\CachePluginBase $cache) {  if ($view->id() === 'front_actualites' && $view->current_display === "page" && \Drupal::request()->query->get('page', 0) === 0) {    $output['#rows'][0]['#rows'][0]['#view_mode'] = 'teaser_pinned';  }}

avec

$view->id() === 'front_actualites' && $view->current_display === "page"

Je teste si je suis bien sur la bonne vue avec le bon mode d'affichage

\Drupal::request()->query->get('page', 0) === 0

Me permet de tester le paramètre GET ?page= (qui gère la pagination) en fournissant la valeur par défaut 0, afin de tester que je sois bien sur la première page.

Enfin je change le view_mode de la première ligne de mes résultats :

$output['#rows'][0]['#rows'][0]['#view_mode'] = 'teaser_pinned';

 

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Views - créer un Pager personnalisé

Pour un projet j'avais besoin d'afficher une pagination particulière sur une vue.

La première page devait afficher 9 éléments, et les suivantes 12.

J'ai donc pour cela créé un Pager personnalisé pour Views, et ajouté ce paramètre en option.

En voici le code, disponible aussi sur github : https://github.com/kgaut/kgaut_tools/blob/2.0.x/src/Plugin/views/pager/…

Ce code est à placer dans un module, au chemin suivant : modules/custom/mon_module/src/Plugin/views/pager/PagerFullWithSpecificFirstPage.php

 10];    unset($options['expose']);    return $options;  }   /**   * {@inheritdoc}   */  public function buildOptionsForm(&$form, FormStateInterface $form_state) {    parent::buildOptionsForm($form, $form_state);    $pager_text = $this->displayHandler->getPagerText();    $form['items_per_page']['#weight'] = -2;    $form['items_per_page_first_page'] = [      '#title' => $pager_text['items per page title'] . ' for the first page',      '#type' => 'number',      '#min' => 0,      '#weight' => -1,      '#description' => $pager_text['items per page description'],      '#default_value' => $this->options['items_per_page_first_page'],    ];     unset($form['expose']);  }   /**   * {@inheritdoc}   */  public function validateOptionsForm(&$form, FormStateInterface $form_state) {    // Only accept integer values.    $error = FALSE;  }   /**   * {@inheritdoc}   */  public function summaryTitle() {    if (!empty($this->options['offset'])) {      if ($this->options['items_per_page'] !== $this->options['items_per_page_first_page']) {        return $this->formatPlural($this->options['items_per_page'], '@count item (@count_first for the first page), skip @skip', 'Paged, @count items, skip @skip', [          '@count' => $this->options['items_per_page'],          '@count_first' => $this->options['items_per_page_first_page'],          '@skip' => $this->options['offset'],        ]);      }      return $this->formatPlural($this->options['items_per_page'], '@count item, skip @skip', 'Paged, @count items, skip @skip', [        '@count' => $this->options['items_per_page'],        '@skip' => $this->options['offset'],      ]);    }    if ($this->options['items_per_page'] !== $this->options['items_per_page_first_page']) {      return $this->formatPlural($this->options['items_per_page'], '@count item', 'Paged, @count items (@count_first for the first page)', [        '@count' => $this->options['items_per_page'],        '@count_first' => $this->options['items_per_page_first_page'],      ]);    }    return $this->formatPlural($this->options['items_per_page'], '@count item', 'Paged, @count items', ['@count' => $this->options['items_per_page']]);  }   /**   * {@inheritdoc}   */  public function query() {    if ($this->current_page === 0) {      $this->options['items_per_page'] = $this->options['items_per_page_first_page'];    }    $limit = $this->options['items_per_page'];    $offset = $this->options['offset'];    if ($this->current_page > 0) {      $offset += ($this->current_page - 1) * $this->options['items_per_page'];      $offset += $this->options['items_per_page_first_page'];    }    if (!empty($this->options['total_pages'])) {      if ($this->current_page >= $this->options['total_pages']) {        $limit = $this->options['items_per_page'];        $offset = $this->options['total_pages'] * $this->options['items_per_page'];      }    }    $this->view->query->setLimit($limit);    $this->view->query->setOffset($offset);  }   public function updatePageInfo() {    if (!empty($this->options['total_pages'])) {      if (($this->options['total_pages'] * $this->options['items_per_page']) total_items) {        $this->total_items = $this->options['total_pages'] * $this->options['items_per_page'];      }    }     // Don't set pager settings for items per page = 0.    $items_per_page = $this->getItemsPerPage();    $items_per_page_first = $this->getItemsPerPageFirst();    if (!empty($items_per_page)) {      // quick fix if specific number on first page      $total_items = (int) $this->getCurrentPage() !== 0 ? $this->getTotalItems() + $items_per_page_first - 1 : $this->getTotalItems();      $pager = $this->pagerManager->createPager($total_items, $this->options['items_per_page'], $this->options['id']);      // See if the requested page was within range:      if ($this->getCurrentPage() >= $pager->getTotalPages()) {        $this->setCurrentPage($pager->getTotalPages() - 1);      }    }  }   public function getItemsPerPageFirst() {    return isset($this->options['items_per_page_first_page']) ? $this->options['items_per_page_first_page'] : 0;  } }

Vous pouvez retrouver

Par kgaut
Kevin Gautreau

Composer - Drupal - résoudre l'erreur "undefined index: extra"

Depuis quelques jours, on peut rencontrer l'erreur «  undefined index: extra » lors d'un composer update ou composer require.

Si vous rencontrez ce soucis, exécutez la commande :

composer update zaporylie/composer-drupal-optimizations --no-plugins

Cela devrait résoudre le soucis.

Plus d'informations ici : https://github.com/zaporylie/composer-drupal-optimizations/pull/22

Par Christophe MOLLET
Christophe Mollet

Comment choisir son CMS pour un projet web ?

Lorsque l'on souhaite réaliser un projet web, il n'est pas toujours facile de choisir le CMS le plus adapté à votre besoin. Pour vous aider à y voir plus clair pour votre projet, notre agence web vous explique les technologies web en détails.

Bien que nos gènes viennent et resterons dans le web, la barrière entre les applications mobiles et les sites web est de plus en plus flou. Technologie front, back, CMS, application native, hybride, PWA, on est vite perdu lorsque nous devons choisir une technologie ou la raison pour laquelle nous choisissons cette technologie.

Par kgaut
Kevin Gautreau

Vidéo mini formation : Installer drupal 9 avec composer

J'ai mis en ligne tard vendredi soir ma première vidéo de mini-formation à drupal.

J'ai commencé par un sujet basique : Le téléchargement et l'installation du CMS Drupal avec composer.

La vidéo est disponible gratuitement sur youtube et dure 20 minutes.

Je ne m'étais jamais confronté avant au format vidéo, et même si j'ai déjà donné des conférence et animé des formations, le format est évidement complètement différent.

Le résultat n'est pas parfait, mais j'en suis assez content. N'hésitez-pas à me faire vos retours sur le fond comme sur la forme.

Si vous souhaitez me suggérer un sujet pour une prochaine vidéo, vous pouvez le faire à la page suivante : https://kgaut.net/suggestion-sujet-video-formation.

Pour info, je suis déjà en train de préparer le conducteur de la prochaine qui présentera la gestion de la configuration et le module config split dans Drupal.

On m'a posé la question du temps passé et de mon organisation, voici en détail les étapes.

J'ai commencé évidement à définir le conducteur et les différents liens à afficher en me servent de notion.so, environ 2h de préparation.

Image

Conducteur drupal 9 avec composer - notion

 

J'ai ensuite fait l'enregistrement avec le logiel OBS, très bon soft, super facile à prendre en main. Il permet de mixer les "scènes" (captation d'un écran, d'une webcam, image fixe) et les sources de son (micro, pc...). La captation m'a pris environ 1h30. J'avais divisé la captation en séquences, une pour chaque chapitre, me permettant de reprendre mon souffle et de boire un peu d'eau entre chaque. Pour chaque séquence, il m'a souvent fallu plus de 5 prises pour en avoir enfin une correcte.

Enfin le montage a été fait avec le logiciel Kdenlive. Ici aussi très facile à prendre en main. Cette phase à consisté à enchaîner les séquences filmées et en faisant des "cuts", quand c'était nécessaire (bafouille, répétition...) Pour cette partie en comptant le visionnage final : 2H00, sans compter le temps de rendu, environ 30 minutes.

Soit un total d'environ 6H pour 20 minutes utiles de vidéo.

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Ajouter un champ Media dans un formulaire et l'afficher dans un template

Voici comment ajouter un champ de type « référence à un media » dans un formulaire personnalisé en utilisant le widget Media Library :

    $form['video'] = [      '#type' => 'media_library',      '#allowed_bundles' => ['video', 'remote_video'],      '#title' => t('Video'),      '#default_value' => $config->get('video') ?? NULL,    ];

la clé #allowed_bundles contient la liste des types de media que l'on veut autoriser

Voici ensuite comment le rendre dans un template via un controller :

Dans le controller :

if($config['video'] && $media = Media::load($config['video'])) {    $config['video'] = $this->entityTypeManager->getViewBuilder('media')->view($media, 'default');}     return [   '#theme' => 'mon_template',   '#data' => $config,];

et dans le template twig, un simple :

{{ data.video }}

 

Par kgaut
Kevin Gautreau

Drupal 8 & 9 - Menu - définir comme parent un élément de menu créé via l'UI

Dans le fichier mon_module.links.menu.yml, on peut facilement placer un élément de menu dans une arborescence via la clé parent.

mon_module.admin_mon_module_config_form:  title: 'Configuration Mon Module'  route_name: mon_module.admin_resalys_config_form  description: 'Configuration webservice de mon module'  parent: system.admin_config_services  weight: 10

Ici le parent fait référence à la clé d'un autre élément de menu.

Mais parfois le parent voulu a été créé via l'interface, et donc le parent doit être référencé via son UUID :

mon_module.admin.structure.settings:  title: 'Partner settings'  description: 'Configure Partner entities'  route_name: partner.settings  parent: 'menu_link_content:4bbda5ed-eeee-4eab-be01-e3d7349f4daa'

L'UUID est trouvable dans la base de données, ou bien en passant par l'onglet devel de l'élément de menu « parent » voulu :

Image

Drupal menu item uuid

Par kgaut
Kevin Gautreau

Drupal 8 & Drupal 9 - Utiliser des vues différentes selon les taxonomies

À partir de drupal 8, c'est une vue qui gère la page des contenus d'un terme de taxonomie.

Le soucis est que c'est la même vue pour l'ensemble des vocabulaires.

Pour le site courant je voulais une page différente pour les catégories de module (exemple avec la catégorie backoffice) de la page par défaut (exemple avec le tag drupal).

Pour cela il faut surcharger le contrôleur gérant les vues, et n'ayez pas peur c'est moins compliqué à faire qu'à dire.

Déclaration du service

Le service va altérer la route /taxonomy/term/% pour changer le contrôleur qui sera executé

modules/custom/mon_module/mon_module.services.yaml

services:  mon_module.route_subscriber:    class: Drupal\mon_module\Routing\TaxonomyListingViewsRoutingSubscriber    tags:      - { name: event_subscriber }

Définition du service

modules/custom/mon_module/src/Routing/TaxonomyListingViewsRoutingSubscriber.php

get('entity.taxonomy_term.canonical')) {      $route->setDefault('_controller', '\Drupal\mon_module\Controller\TaxonomyTermViewPageController::handle');    }  }   public static function getSubscribedEvents() {    $events = parent::getSubscribedEvents();    $events[RoutingEvents::ALTER] = array('onAlterRoutes', -180);    return $events;  } }

Surcharge du contrôleur

modules/custom/mon_module/src/Controller/TaxonomyTermViewPageController.php

getParameter('taxonomy_term');     $vid = $term->get('vid')->first()->getValue();    $vid = $vid['target_id'];     //on teste le vocabulaire    if ($vid === 'modules_categories') {      $view_id = 'front_modules';      $display_id = 'page_categories';    }     return parent::handle($view_id, $display_id, $route_match);  } }

 

Par kgaut
Kevin Gautreau

Drupal - Améliorer l'expérience d'administration

Note : Quand on débute avec drupal, le plus dur est de connaître l'ensemble de l'écosystème. Entre-autres, l'ensemble des modules tiers (contrib) qui existent. J'inaugure une nouvelle section sur ce site avec la présentation (rapide) de différents modules que je conseille. Cela se passe sur la page Modules Drupal. Je vais tenter aussi de temps en temps de mettre en avant des modules par thématique. c'est le cas ici.

Le backoffice de drupal est réputé pour être austère, même si cela s'est amélioré depuis les dernières version, ça n'est pas complètement faux !

Mais via quelques modules et thème il est possible d'améliorer grandement cette expérience.

Vous trouverez ci-dessous quelques modules que je conseille dans cet objectifs. N'hésitez-pas à ajouter les votre.

Adminimal

 Backoffice
 Thème

Le thème d'administration que j'utilise sur la plupart de mes sites. En en faisant un thème enfant évidement.

Lire la suite de Adminimal

 

Admin Toolbar

 Backoffice
 Indispensable

Admin Toolbar transforme la barre d'administration native de drupal (module toolbar) en menu déroulant. Oui ce module est indispensable.

À noter, ce module contient différents sous-modules :

Lire la suite de Admin Toolbar

 

Coffee

 Backoffice
 Indispensable

Le module Coffee permet aux administrateur de déclencher via un raccourci clavier (alt+k ou alt+d) un petit moteur de recherche qui indexe tous les éléments du menu d'administration.

Ainsi, plus besoin de chercher pendant des heures où est cachée la page de configuration de cet obscure module.

Lire la suite de Coffee

 

Field Group

 Backoffice

Permet de regrouper des champs dans des onglets sur les écrans de création de contenu.

Il est ainsi possible d'avoir des formulaires mieux « rangés »

Lire la suite de Field Group

 

Module filter

 Backoffice

Quand on commence à avoir une liste de module importante, la page par défaut n'est pas très utilisable.

Ce module permet de regrouper les modules par package dans des onglets et ajoute un champ de recherche javascript.

Lire la suite de Module filter

 

Environment Indicator

 Backoffice

Ce module tout simple permet de modifier la couleur de la barre d'administration en fonction d'un environnement (prod / preprod / staging / dev...)

La sélection de l’environnement courant peut se faire de plusieurs façon, le plus simple à mon sens est via le fichier settings.local.php :

Lire la suite de Environment Indicator

 

Node Edit Protection

 Backoffice
 Rédaction contenu
 Indispensable

Il vous est déjà arrivé de travailler sur un contenu pendant un certain temps sur votre site, et de quitter la fenêtre par erreur ou bien de faire précédent, et ainsi de perdre tout ce que vous aviez fait ? Moi oui...

Ce module ajoute une demande de confirmation javascript dans ces cas là ❤️

Lire la suite de Node Edit Protection

Par Q__nt_n
Quentin Fonteneau

Présentation du module drupal Config Pages

Présentation du module drupal Config Pages
Q__nt_n
ven 11/09/2020 - 13:20

Dans mes précédents sites Drupal, il m'arrivait de développer un formulaire de configuration pour le site (réseaux sociaux, paramètres métier etc...), cela pouvait s'avérer complexe et chronophage dès que le client voulait une personnalisation de ce formulaire. Pour information pour cette solution orientée "développement", vous pouvez retrouver la documentation ici

Cependant, dernièrement, Raphaël Morvan m'a parlé de Config Pages. Ce module fournit une entité qui permet de créer des pages de configuration personnalisables et riches en fonctionnalités et de les placer où l'on souhaite dans le système de menu. On peut également utiliser les widgets de Drag & Drop, Autocomplétion etc... Le gain de temps peut donc devenir très important face à une page de configuration développée de A à Z.

Désormais, j'utilise ce module afin de m'éviter également la création d'un type de contenu 'Homepage'. En effet, généralement, ce type de contenu est instancié une seule et unique fois (Et désormais ça me parait être une aberration de le faire). Chose à noter, les pages de configuration créées grâce à ce module prennent en compte le contexte de langue. 

Voici le formulaire de création d'un type de page de config :

Capture d'écran du formulaire de création d'un type de page de config

Ensuite, la page de config étant une entité, nous pouvons gérer les champs comme dans un type de contenu !

Capture d'écran du formulaire crée

Thématique

Par Q__nt_n
Quentin Fonteneau

Création d'un environnement de développement Drupal (Partie 1)

Création d'un environnement de développement Drupal (Partie 1)
Q__nt_n
jeu 27/08/2020 - 09:34

Création d'un environnement de développement Drupal

Cela fait un certain temps que je souhaitais écrire un article sur la mise en place d'un environnement de développement pour Drupal. Afin de rester le plus clair possible je vais décomposer le sujet en plusieurs parties :

  1. La mise en place d'un environnement de développement via Lando
  2. La création d'un socle Drupal (à venir)
  3. L'utilisation du socle, workflow et bonnes pratiques (à venir)

Mise en place d'un environnement de développement via Lando

Fût un temps, les environnements de développement se résumait à l'installation d'un WAMP / LAMP / MAMP avec les bonnes versions des différents technos (PHP, node etc... ) et c'était tout de même assez fastidieux car il fallait que cette installation et cette configuration soient réalisés sur chacun des postes des développeurs.

Depuis quelques temps maintenant, un petit nouveau est apparu : Docker. Ce dernier permet, entre autres, d'échapper à cette gestion des dépendances et autres extensions... Mais ceci dit, il faut tout de même le faire dans le Dockerfile.

Lors de mon précédent travail, chez Capgemini, nous avions mis en place un environnement de développement à l'aide de Docker. Cependant, je trouvais qu'on avait atteint une certaine limite car je trouve que Docker n'est pas forcément accessible et compréhensible au premier abord. Par ailleurs, j'avais testé le couple traefik et portainer pour gérer plus facilement mes containers / sites mais j'ai rapidement arrêter car c'était assez complexe et trop coûteux en temps.

En arrivant dans mon nouveau travail chez Digital Garden, j'ai pu avoir le temps nécessaire de mettre en place un environnement de développement. Pour cela je me suis basé sur Lando, que j'avais découvert à Capgemini (Merci Nasser ! ;) ). Je l'avais survolé et malheureusement passé à côté du but précis de Lando. 

Lando est une surcouche de Docker permettant de mettre en place, sans effort, un environnement, d'automatiser certaines tâches (les tests par exemple).

La documentation est plutôt bien faite ! Des "recettes" (Drupal 8, Wordpress etc...) sont déjà disponibles permettant d'être encore plus rapide pour installer un environnement. 
Par exemple, la recette Drupal met à disposition Nginx / Apache, MySQL / MariaDB, Composer, Drush... 

La configuration de Lando se résume à la création d'un fichier .lando.yml à la racine du projet.
 

name: socle-drupal

recipe: drupal8

config:
  php: '7.4'
  via: apache
  webroot: web
  xdebug: true

services:
  appserver:
    php: '7.4'
    config:
      php: .lando/config/php.ini
  database:
    type: mariadb:10.1
  phpmyadmin:
    type: phpmyadmin
    hosts:
      - database
  mailhog:
    type: mailhog
    hogfrom:
    - appserver
  node:
    type: node
    globals:
      gulp-cli: "latest"

tooling:
  vim:
    service: appserver
  npm:
    service: node
  node:
    service: node
  gulp:
    service: node
    description: "Compiles SASS"
    cmd: gulp

Décomposons ce fichier .lando.yml !

  • name: socle-drupal correspond au nom du projet. Par défaut, ce nom sera utilisé pour construire l'URL par défaut du site (socle-drupal.lndo.site).
  • recipe: drupal8 est la recette utilisée, nous ne sommes pas obligés d'en utiliser. Des recettes sont disponibles pour Wordpress, Drupal 6, 7, 8, Laravel, Python, Ruby... 

S'en suit différents "blocs" :

  • config permet de modifier la configuration et d'avoir quelque chose qui répond vraiment à nos besoins : version du PHP, activation de xdebug, apache/nginx
  • services permet l'ajout de services prédéfinis par Lando, d'y gérer une config particulière de l'application (par l'ajout d'un php.ini custom par exemple).
    Je recommande fortement d'ajouter le service `phpmyadmin` et de le brancher sur la base de données, cela évitera de devoir gérer les ports de la base de données (qui, s'ils ne sont pas fixés, seront aléatoires à chaque démarrage du projet.
  • La partie tooling permet d'ajouter des commandes à l'outil lando. Dans notre cas, nous pourrons, par exemple, exécuter la commande lando npm install. Npm étant branché sur le service node, Lando exécutera la commande dans le container node. Nous verrons dans la dernière partie de cette série d'article, l'ajout d'outil très utile pour suivre la qualité du code et le suivi des bonnes pratiques Drupal.

La création de la base de notre environnement de développement est désormais terminée. Nous verrons dans un prochain article, la création du socle Drupal afin d'avoir un environnement de développement Drupal complet !

Thématique

Par kgaut
Kevin Gautreau

Drupal 8 et Drupal 9 - Créer une archive Zip

Voici comment créer une archive zip et y ajouter des fichiers.

Pour commencer, nous aurons besoin d'injecter deux services, (ici je suis dans un formulaire, à adapter en fonction du contexte)

  public static function create(ContainerInterface $container) {    $instance = parent::create($container);    $instance->fileSystem = $container->get('file_system');    $instance->archiver = $container->get('plugin.manager.archiver');    return $instance;  }

Création de l'archive en tant que telle :

// $files contient un tableau de chemin de fichiers à ajouter à l'archive$files = ['private://monfichier1.pdf', 'private://monfichier2.pdf']; // Chemin où sera enregistré l'archive$archivePath = 'private://pdf-export/mon-archive.zip'; // On crée l'archive physiquement sur le disque, avec un contenu vide// FileSystemInterface::EXISTS_REPLACE indique que si le fichier existe déjà on le remplace, il existe d'autres options$this->fileSystem->saveData('', $archivePath, FileSystemInterface::EXISTS_REPLACE); //On récupère l'objet Zip pointant vers l'archive que nous venons de créer./** @var Zip $zip */$zip = $this->archiver->getInstance(['filepath' => $archivePath]); foreach ($files as $file)  {  $filepath = $this->fileSystem->realpath($file);  $zip->add($filepath, $filename);}

Et voila, cela fonctionne mais ça n'est pas forcément top... Pourquoi pas forcément top ? car toute l’arborescence des différents fichiers est recrée dans l'archive :

Image

Archive Zip

 Ça n'est pas forcement très gênant, mais dans mon cas je préférais avoir tous les fichiers à la racine de mon archive. Voila comment faire :

// $files contient un tableau de chemin de fichiers à ajouter à l'archive$files = ['private://monfichier1.pdf', 'private://monfichier2.pdf']; // Chemin où sera enregistré l'archive$archivePath = 'private://pdf-export/mon-archive.zip'; // On crée l'archive physiquement sur le disque, avec un contenu vide// FileSystemInterface::EXISTS_REPLACE indique que si le fichier existe déjà on le remplace, il existe d'autres options$this->fileSystem->saveData('', $archivePath, FileSystemInterface::EXISTS_REPLACE); //On récupère l'objet Zip pointant vers l'archive que nous venons de créer./** @var Zip $zip */$zip = $this->archiver->getInstance(['filepath' => $archivePath]); // $zip est juste un wrapper Drupal vers un objet \ZipArchive que l'on va récuprérer ici dans $zipArchive$zipArchive = $zip->getArchive(); foreach ($files as $file)  {  // on récupère le nom de fichier, sans le chemin  $filename = basename($file);  $filepath = $this->fileSystem->realpath($file);   // on ajoute le fichier à l'archive mais en indiquant comme chemin uniquement le nom du fichier, ainsi, plus de création de dossier  $zipArchive->addFile($filepath, $filename);}

 

Pages