Planète

Par juliendubreuil
julien dubreuil
Drupal since 2009

Liste des modules pour Drupal Commerce

Liste de module pour Drupal Commerce

Que vous soyez à la recherche d’un module pour un but précis, ou simplement à la recherche de nouvelles fonctionnalités pour votre site e-commerce il n’est pas simple de s’y retrouver dans tous ces modules. Ainsi chercher un module peu vite s’avérer long et fastidieux. Voici donc une petite liste de modules qui vous permettront de trouver votre chemin.

Comme avec le guide des modules pour Drupal 7, j’ai reparti les modules dédiés au e-commerce en différentes sections. Cette liste est une liste collaborative, ainsi, n’hésitez pas à commenter si vous trouvez des modules à ajouter !

E-commerce

Drupal Commerce est une suite de modules dédié au e-commerce qui s’est imposée sur Drupal.

Commerce VAT et Commerce EU VAT pour gérer les taxes en fonction du pays de votre client. Pensez à ne pas utiliser le module Taxe si vous utilisez ces deux modules. Ils sont bien maintenus et prennent en comptes les changements de lois.

Address book pour simplifier la vie de vos clients en leur permettant de créer un carnet d’adresse de façon ce qu’ils réutilisent leurs adresses dans le tunnel d’achat.

Shipping fourni un système de calcul de prix de frais de port. Ce module est un framework que vous devrez utiliser avec les modules de livraisons de votre transporteur préféré.

Commerce billy et Commerce billy mail pour générer des factures depuis votre site au format PDF afin que vos clients puissent les télécharger.

Commerce search api couplé à Search api vous permettra d’indexer les entités Drupal Commerce dans un moteur de recherche. Vous pourrez alors construire vos pages de catalogues robustes et performantes.

Search API ranges pour donner à vos visiteurs la possibilité de réduire les résultats entre deux prix.

Commerce backoffice afin d’avoir une meilleure expérience utilisateur dans la gestion et l’administration de votre boutique. Si vous avez déjà installé Commerce Kickstart v2 vous avez pu noter que le backoffice était plus beau que la version précédente et c’est principalement grâce à ce module.

Commerce reports permet aux marchands d’avoir une idée de ce qu’il se passe sur leur site. Ce module fourni un dashboard de statistiques sur l’évolution des commandes, clients…

Google analytics pour Commerce couplé à Google analytics vous permettra de traquer vos ventes en plus de vos statistiques de visites depuis le dashboard Google Analytics.

Cart and Checkout

Commerce checkout redirect redirige vos utilisateurs non connectés vers une page de création de compte ou de login avant de rentrer dans le tunnel d’achat. Notez que forcer les utilisateurs à créer un compte peut être source d’abandon de paniers.

Commerce checkout complete registration offre la possibilité aux clients de compléter le tunnel d’achat en anonyme et de créer un compte après le paiement s’ils le souhaitent.

Commerce checkout progress affichera dans votre checkout une barre en haut de la page de façon à indiquer à vos clients à quelle étape du checkout ils sont.

Commerce cart expiration permet de définir une durée de validité pour un panier. Par défaut les paniers n’ont pas de limite de validité, or il peut être intéressant d’en définir une de façon à faire un peu de ménage tous les 15 jours. Ce module vous permettra de supprimer les paniers selon vos critères. Pensez à avertir vos clients qu’ils ont laissé un panier histoire de les relancer.

Commerce add to cart confirmation affichera une pop-in lors de l’ajout au panier d’un produit. Cette pop-in offrira à vos clients le choix de poursuivre leurs achats ou de commencer le tunnel d’achat.

Commerce ajax cart donnera à vos clients la possibilité d’avoir une idée de leur panier via un bloc en Ajax. Cette fonctionnalité est bien moins intrusive que la pop-in de confirmation.

Products

Commerce stock vous aidera à gérer vos stocks et ainsi les produits disponibles à la vente.

Commerce stock notification pour ne plus louper une vente lorsque vous êtes en rupture de stock ! En cas de rupture de produit, le champ stock de la fiche de produit sera remplacé par un formulaire afin que le client puisse soumettre son email afin d’être notifié lors du réapprovisionnement.

Retour de marchandises Il arrive des fois où tout ne se passe pas comme prévu dans la vente à distance. Ce module permettra à vos clients de faire une demande de retour de leurs produits directement depuis leur commande. Notez qu’avec la loi Hamon, vous devez permettre à vos clients de pouvoir faire une demande de retour via un formulaire sur votre site.

Wishlist est utile lorsque vous voulez permettre à vos visiteurs de créer des listes d’envies afin d’acheter plus tard. Un lien permettant d’ajouter le produit dans une liste de souhaits sera inséré sur chaque fiche produit. L’utilisateur pourra par la suite retrouver ses produits préférés depuis son compte.

Commerce productpopularity vous permettra d’afficher un bloc des produits les plus populaires sur votre site.

Commerce rec permet d’afficher une liste de produits que d’autres clients ont acheté en commandant ce produit. Pratique pour booster le panier moyen, cette fonctionnalité vous demandera d’avoir déjà réalisé un certain nombres de vente pour être efficace.

Marketing

Discounts et Coupon 2.x vous permettront de créer des offres spéciales pour vos clients. Vous pourrez alors faire des réductions sur des produits, des profils de clients avec ou sans limite dans le temps.

Imports / Exports

Si vous utilisez le module Features pour exporter la configuration de votre site, vous pourrez ajouter Commerce Features à votre projet pour exporter ce qui est relatif au module Commerce.

Deux solutions s’offrent à vous si vous voulez importer des données. La plus simple des solutions consiste à utiliser Feeds, Commerce Feeds, voir même Xpathparser pour Feeds. Simple et efficace vous pourrez créer un import pas trop compliqué.
Si vous souhaitez quelque chose de plus robuste je vous conseille d’utiliser le couple Migrate et Commerce Migrate. Plus compliqué à prendre en main vous pourrez néanmoins réaliser des imports/exports complexes.

Performances et développement

Commerce Devel et un module additionnel pour Devel qui vous aidera dans votre développement de tous les jours.

Commerce Entitycache vous permettra en complément de Entitycache d’améliorer les performances de votre site en ajoutant aux caches les entités Drupal Commerce.

Par LaboRouge

function drupal_map_assoc()

Fonction méconnue, drupal_map_assoc() permet de former un tableau associatif à partir d'un tableau linéaire.

&lt;?php<br />
$fruits = array('pommes', 'oranges');<br />
drupal_map_assoc($fruits);<br />
// $fruits est maintenant un tableau ('pommes' =&gt; 'pommes', 'oranges' =&gt; 'oranges')<br />
?&gt;

Trés pratique lorsque l'on veut assigner une clé | valeur de manière automatique. 


Drupal


Drupal API


Fonctions

Par LaboRouge

function drupal_map_assoc()

Fonction méconnue, drupal_map_assoc() permet de former un tableau associatif à partir d'un tableau linéaire.

&lt;?php

$fruits = array('pommes', 'oranges');
drupal_map_assoc($fruits);
// $fruits est maintenant un tableau ('pommes' =&gt; 'pommes', 'oranges' =&gt; 'oranges')

?&gt;

Trés pratique lorsque l'on veut assigner une clé | valeur de manière automatique. 


Drupal


Drupal API


Fonctions

Par vincent59
Vincent Liefooghe

Paramétrage fail2ban pour Drupal

Par défaut, Drupal permet de "bannir" des adresses IP manuellement, via la console d'administration.

Ceci est une première étape, mais peut vite devenir ingérable sur un site fréquenté. De plus, les requêtes http vont quand même arriver jusqu'au CMS, puisque c'est lui qui gère ces exclusions.

La solution dans ce cas est de coupler les logs Drupal avec fail2ban, qui s'occupe de scruter différents fichiers logs et de repérer, via des expressions régulières, des motifs qui permettront de bannir des adresses IP au niveau systèmes, via des règles iptables.

Pré-requis

Pour que fail2ban fonctionne avec Drupal, il faut activer le module syslog, et le paramétrer pour qu'il envoie ses logs dans un fichier séparé (voir à ce sujet http://www.vincentliefooghe.net/content/drupal-activer-les-traces-en-syslog).

Installation et paramétrage de fail2ban

Sur une distribution debian-like, simplement :

sudo apt-get install fail2ban

Les fichiers de configuration se trouvent dans /etc/fail2ban :

  • fail2ban.conf : fichier de configuration assez général, avec le fichier de logs de fail2ban, le niveau de logs et le socket unix
  • jail.conf : définition des "jails", c'est à dire les paramètres et les actions par défaut. Il est fortement recommandé d'en faire une copie dans un fichier jail.local pour éviter l'écrasement lors des mises à jour
  • filter.d : répertoire dans lequel se trouvent les définitions des filtres (regex)
  • action.d : répertoire dans lequel se trouvent les définitions des actions.

Dans le fichier jail.local, je vous recommande de renseigner la ligne

ignoreip = 127.0.0.1/8

et d'ajouter les adresses IP de vos PC clients ou serveurs, ce qui peut vous éviter un "auto-bannissement" en cas de manipulations douteuses sur le ssh, des accès à des scripts php, etc.

Paramétrage propre à Drupal

Pour bannier des adresses IP qui tenteraient des connexions, on ajoute un fichier de définition de règle dans le répertoire filter.d. Par exemple, drupal-user.conf, qui va contenir :

# filter to ban IP for Login attempt
[Definition]
failregex = \|user\|<HOST>\|.*\|Login attempt failed (.+)\.$
#failregex = \|user\|<HOST>\|.*spambot.Failed (.+)\.

ignoreregex =

Le filtre recherche les occurences de "Login attempt failed". La ligne est formatée avec *|user|adresse IP|*. Par exemple (cas réel) :

Sep 24 10:03:12 vps35701 drupal: http://www.vincentliefooghe.net|1411545792|user|89.66.70.109|http://www.vincentliefooghe.net/?q=user/login|http://www.vincentliefooghe.net/?q=user/login|0||Login attempt failed for Marcferozi.

Puis dans le fichier jail.local, on ajoute une nouvelle section :

#
# Drupal
#
[drupal-user]
enabled = true
port = http,https
protocol = tcp
filter = drupal-user
logpath = /var/log/drupal.log
maxretry = 3
# 1 hour
findtime = 3600
# 20 hours
bantime = 72000

On déclare donc à fail2ban qu'il doit utiliser le filtre drupal-user, sur le fichier de logs /var/log/drupal.log. Dans celui-ci, il cherche pendant une heure maximum 3 tentatives de connexions.

Si fail2ban détecte une "attaque", il le signale dans ses logs :

2014-09-23 00:41:56,025 fail2ban.actions: WARNING [drupal-user] Ban 89.66.70.109

On peut également voir les adresses IP bannies via iptables -L :

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
fail2ban-drupal-user  tcp  --  anywhere             anywhere             multiport dports http,https
fail2ban-ssh-ddos  tcp  --  anywhere             anywhere             multiport dports ssh
fail2ban-ssh  tcp  --  anywhere             anywhere             multiport dports ssh
ACCEPT     all  --  was59-4-88-175-36-21.fbx.proxad.net  anywhere            

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain fail2ban-drupal-user (1 references)
target     prot opt source               destination         
DROP       all  --  qqa.1113460302.com   anywhere            
DROP       all  --  seo7.heilink.com     anywhere            
RETURN     all  --  anywhere             anywhere  

On peut également ajouter d'autres règles, si l'on a mis en place des modules de type honeypot ou botcha, pour bannier les spammeurs.

 

Catégorie: 


Tag: 

Par juliendubreuil
julien dubreuil
Drupal since 2009

Développeurs, êtes-vous prêts pour Drupal 8 ?

Développeurs, soyez prêt pour Drupal 8 et les dernières nouveautés ajoutées au CMS

“It’s ready, when it’s ready!” Nous connaissons tous cette citation, qui fait partie de la communauté Drupal. Ce n’est un secret pour personne, Drupal 8 sera bien plus compliqué à appréhender que les versions précédentes. Malheureusement il est encore impossible de définir une date de sortie, néanmoins on peut se dire qu’il nous reste encore quelques mois devant nous. Utilisons ainsi ce temps pour comprendre et découvrir tous les pré-requis de cette nouvelle version.

Depuis quelques mois maintenant j’ai commencé à agréger une liste d’articles de façon à être au point sur les dernières nouveautés apportées par D8. Du coup, voici un résumé des meilleurs ressources que j’ai pu trouver.

Notez que dans ce billet je ne parlerai pas des nouveautés de Drupal 8 mais plutôt des bases à avoir pour commencer à développement avec Drupal 8.

Programmation PHP moderne

POO, Classes et objets.

La Programmation Orientée Objet n’a rien de nouveau et s’est standardisée dans tous les languages de développement et frameworks, pourtant c’est assez nouveau dans le monde de Drupal. Jusqu’à maintenant réservée à quelques spécificités, la POO fera partie intégrale de Drupal 8. Rien de tel que de revoir ses bases pour être au courant des dernières nouveautés, voici quelques liens qui pourront vous aider.

Ressources :

Namespace

Les namespaces sont arrivés avec PHP 5.3 afin de résoudre deux problèmes avec les fonctions et les classes quand on veut écrire du code réutilisable. Premièrement, la collision de même nom entre portion de code et deuxièmement la possibilité de créer des alias pour les noms longs de fonction ou de classe à rallonge. Avec cette nouvelle version de Drupal, vous trouverez l’utilisation des namespaces un peu partout.

Ressources :

Autoloaders, PSR-0 et PSR-4

PSR–0 est un standard de chargement de classe (l’autoloading) permettant de définir une façon automatique d’inclure des classes PHP sans avoir à utiliser les fonctions telles que require() ou include(). PSR-0 se base sur le namespace d’une classe afin de définir sa position dans le file system.

PSR-4 a pour objectif de réduire la structure des dossiers pour l’autoloader. Cela a fait couler pas mal d’encre dans sur drupal.org – Drupal and PSR-0/PSR-4 Class Loading.

Ressources :

Annotations

Les annotations sont des meta-datas qui peuvent être ajoutées à votre code afin d’être lues lors de l’execution de celui-ci. Cela sert pour la définition et la configuration mais les annotations n’affectent pas la sémantique du code.

Personnellement je ne suis pas un grand fan des annotations, pour moi les commentaires doivent rester des commentaires, de façon à donner des informations et de la description sur le comportement d’une fonction. Changer un commenta ire ne devrait pas changer le comportement d’une fonction.

Ressources :

Design patterns

Il y a de fortes chances que si vous avez à résoudre un problème de conception, un autre développeur l’ait déjà résolu. Ainsi les design patterns (En) sont des guides applicables à n’importe quel language de programmation de façon à solutionner un problème. L’avantage d’utiliser des design patterns est que cela rend votre code plus maintenable, et que cela offre une solution éprouvée.

Ressources :

Dependency injection

C’est certainement l’un des design patterns les plus compliqués à bien expliquer, toutefois c’est probablement l’un des plus simple à utiliser. Ok, le nom est effrayant, mais je suis sûr que vous l’appliquez sans le savoir. Pour bien comprendre ce que c’est, je vous suggère de regarder comment Fabien Potencier l’explique.

Ressources :

Symfony 2

Ce n’est plus la nouvelle de l’année, Drupal 8 utilisera quelques composants tout droit venus de Symfony 2. Comme souvent il y a du pour et du contre sur le fait d’apprendre Symfony 2 pour faire du Drupal 8. Personnellement, Je dirais qu’y jeter un coup d’oeil avant de jouer avec D8 ne vous tuera pas, au contraire cela vous permettra certainement de mieux comprendre certain concepts. En bref, quelques heures à regarder et découvrir Symfony 2 est un bon investissement.

Ressources :

Si vous souhaitez aller un peu plus loin et avoir une vue plus complète des composants SF2 inclus dans Drupal 8, je vous suggère de lire cet article you got Symfony in my Drupal 8 (En) ou celui-ci Symfony 2 in Drupal 8 (En)

Bibliothèques additionnelles ajoutées à D8

En plus de ce que l’on a déjà pu voir, Drupal 8 inclura des librairies externes de façon à ne pas réinventer ce qui existe. D’ailleurs quelques-unes de ces librairies sont utilisées dans l’écosystème Symfony.

Composer

Composer est un outil de gestion des dépendances qui a été adopté en D8 pour gérer les bibliothèques additionnelles telles que Symfony ou encore Twig.
A la racine de Drupal vous pourrez apercevoir un fichier nommé composer.json, structuré d’une certaine manière de façon à définir les dépendances ainsi que leurs versions.

Ressources :

Doctrine

Doctrine Database Abstraction Layer (DBAL) est une surcouche d’abstraction entre l’application et la base de données. Cette API permet de s’interfacer simplement avec la plupart des système de base de données.

N’ayez crainte, Doctrine ne fait pas vraiment parti de Drupal 8 (certainement pour D9), Drupal 8 utilise seulement le composant de lecture des annotations de Doctrine (Cf le commentaire de Chx sur drupal.stackexchange).

Ressources :

Easy RDF

EasyRdf est une bibliothèque PHP permettant de manipuler simplement et efficacement du RDF.

Ressources:

Guzzle

Guzzle est un client HTTP en Php qui permet de s’interfacer avec des applications tiers mettant à disposition des web-services.

Ressources :

PHPUnit

PHPUnit est un framework de test P-Sour PHP. Celui-ci à été choisi en remplacement de Simpletest comme nouvel outil afin d’assurer la qualité du code de D8. On ne le répétera jamais assez, mais les tests unitaires sont la meilleure méthode pour détecter une regression sur un projet.

Ressources :

Twig

Au revoir PHPTemplate, bonjour Twig! PHPTemplate nous aura bien rendu service mais il est maintenant temps pour lui de prendre sa retraite. Twig est un moteur de template moderne pour PHP qui peut être enrichi via un API. Il est facile d’utilisation, sécurisé et flexible.

Ressources :

YAML

Au lieu d’utiliser notre format de fichier maison, la décision a été prise pour D8 d’utiliser quelque chose de plus commun et standardisé. YAML est un standard pour la création de fichier de configuration.

Ressources :

Le mot de la fin

J’ai essayé de ne pas donner trop de lien et de sélectionner les meilleurs pour chaque sujet parce que cela représente beaucoup d’informations. Néanmoins si vous voulez proposer un article pour complémenter ce post, ce sera avec plaisir. En regardant la longueur de ce post de pré-requis, on peut dire que Drupal 8 a changé et est différent des précédentes versions. Cela montre aussi la volonté de plus de qualité et de professionnalisme de la part de la communauté.

Je continuerai de mettre à jour cette liste et en attendant, j’espère que vous y trouverez votre bonheur.

Par admin

Appel à conférencier Drupagora 2014

Pour l'édition 2014, Drupagora remet le couvert pour sa quatrième édition au MAS, Paris 13e. Il se déroulera le 14 novembre prochain autour du CMS Drupal.

L'événement invite les différents acteurs, utilisateurs, contributeurs... à se retrouver pour parler des thématiques proposées :
- Plateformes digitales globales : distributions et usines à sites
- E-commerce avec Drupal pour les sites à > 5M€ de CA
- Applications métier avec Drupal (intranets, RSE, bases documentaires, outils métiers)
- Drupal face à la concurrence des principaux éditeurs (Oracle, Adobe, Sitecore, Ektron, Demandware, Hybris...)

De plus, le fil rouge du programme sera : Drupal dans l'entreprise : projets complexes et intégrés au SI, plateformes digitales globales, sites e-commerce et applications métier.

Si un des thèmes proposés, vous intéressent, alors n'hésitez pas à le proposer dans l'appel à conférencier qui est en cours actuellement.

Par ailleurs, vous pouvez dès à présents consulter le site de l'événement et vous inscrire

site officiel : Drupagora 2014
Inscription à Drupagora 2014

Par AlanT
Alan Tondelier

Présentation du multi-site avec Drupal : Meetup Drupal Rouen #3

Le 17 septembre se tiendra le 3ème Drupal meetup de Rouen. Ce sera l'occasion pour moi de parler de la réalisation de "multi-sites" avec Drupal.

Au programme 

  • Multisite Drupal "classique"
    - Présentation
    - Structure d'un multisite
    - Fonctionnalités partagées / Fonctionnalités spécifiques. Astuces.
     
  • Domain Access
    - Présentation / Usages
    - Mise en place d'un domain access
    - Exemples
     
  • Conclusion : Forces et inconvénients de ces deux approches.

La présentation sera retransmise en live sur youtube, et visionnable ci dessous

 

Par bisonbleu

Step by step guide to installing Commerce from scratch

Step by step guide to installing Commerce from scratch

The following guide was used in a presentation at DrupalCamp Montreal 2014. The presentation focused on how to install Drupal Commerce from scratch (i.e. without the Commerce Kickstart distribution). This is an elevator guide. So get in now because we're going up!

FIRST FLOOR

  1. https://www.drupal.org/project/commerce
  2. Install & enable required modules
    • drush dl ctools views entity rules addressfield commerce
    • drush en ctools views entity rules addressfield commerce -y

ren.admin
dim 14/09/2014 - 14:50

Par bisonbleu

Step by step guide to installing Commerce from scratch

Step by step guide to installing Commerce from scratch

The following guide was used in a presentation at DrupalCamp Montreal 2014. The presentation focused on how to install Drupal Commerce from scratch (i.e. without the Commerce Kickstart distribution). This is an elevator guide. So get in now because we're going up!

FIRST FLOOR

  1. https://www.drupal.org/project/commerce
  2. Install & enable required modules
    • drush dl ctools views entity rules addressfield commerce
    • drush en ctools views entity rules addressfield commerce -y

ren.admin
dim 14/09/2014 - 14:50

Par DuaelFr
Edouard Cunibil
(ex-)Organisateur de meetups && Co-organisateur du DrupalCamp Montpellier 2014 && Co-organisateur des Drupal Developer Days Montpellier 2015 && Core Contributeur && Module Maintainer && Support sur IRC && Lobbying Twitter && etc. ;)

Installer drush sur un serveur mutualisé OVH.

Drush est devenu un outil incontournable pour le développeur Drupal.

Développer son propre plugin Drupal Crumbs

Le module Drupal Crumbs permet de maitriser son fil d’Ariane (Breadcrumb) selon des critères très complets (views, token, taxonomy, entity reference, etc). Son système de plugins lui permet de supporter toute entité créée sur votre site Drupal. Découvrons comment implémenter notre propre plugin Crumbs pour supporter une entité sur mesure ou encore fournie par un module contribué.

Par AlanT
Alan Tondelier

Les formulaires multi-étapes en ajax avec le module mforms

Les fomulaires multi-étapes permettent de récupérer de nombreuses informations sur l'utilisateur sans l'assomer d'un coup avec une grande quantité d'informations. Pour les besoins d'un projet, je me suis penché sur la réalisation d'un formulaire multi-étapes (ou multi-steps en anglais) sous Drupal.

Je recherchais une solution permettant d'afficher une barre de progression, un nombre variables d'étapes, un comportement en ajax, et la possibilité d'accéder facilement aux données utilisateur saisies. Des solutions envisagées j'ai retenu le module mforms. Le résultat de son implémentation est visible ici.

Retour sur expérience.

Un module complet

Installation & exemple

On commence par récupérer le module.

drush dl mforms -y

Une fois téléchargé, le module mforms est livré avec un module d'exemple complet que je vous recommande d'activer.

drush en mforms,mforms_example -y

Au moment de l'écriture de cet article, il existe un bug dans le module d'exemple concernant la gestion de plusieurs formulaires mforms sur la même page. Si vous rencontrez le soucis, utilisez le module developpement.

Un module, deux approches

Si vous avez activé le module d'exemple, vous pouvez vous rendre sur la page <votredrupal>/mforms pour une page d'exemple de mise en place de votre module.

Le module mforms propose deux façons pour gérer le contenu de vos formulaires multi-étapes :

  1. Un stockage dans la variable $form_state ( variable Drupal de stockage des champs de formulaires )
  2. Un stockage des données dans une varriable de SESSION.

Ces deux approches diffèrent principalement au niveau de la persistance des variables.
En effet les variables stockées avec $form_state seront rafraichies au chargement de la page, l'utilisation de $form_state est à privilégier pour les formulaires multi-étapes "simple" ou la deuxième étapes du fomulaire est une étape de validation de données (par exemple).

À contrario, l'utilisation d'une variable de SESSION va permettre de réutiliser les données saisies par l'utilisateur sur l'ensemble du site Internet mais également de revenir sur les saisies effectuées. C'est cette approche que j'ai retenu pour mon projet.

Construction d'un formulaire multi-étapes

Notre formulaire multi-étapes va faire l'objet d'un module personnalisé. Pour cet exemple je vais appeler le module multi_etapes. Comme nous allons traiter de la réalisation d'un module Drupal, le code sera commenté en anglais pour être en accord avec les bonnes pratiques Drupal. Si certains bouts de codes ne sont pas clairs, n'hésitez pas à poser vos questions en commentaire.

Les bases du module

Je ne reviens pas sur les bases d'un module Drupal, tout est bien expliqué sur la documentation officielle.

multi_etapes.info 

name = Multi Etapes
description = Add a multi-steps session based form. Built with the mforms module.
core = 7.x
; Package name
package = Alan
; Module dependencies
dependencies[] = mforms

Ensuite le fichier .module :

multi_etapes.module

&lt;?php

/**
* @file
* Add a multi-steps session based form. Built with the mforms module.
*/

/**
* Implements hook_permission().
*/
function multi_etapes_permission() {
  return array(
    // 'administer my module' =&gt;  array(
    // 'title' =&gt; t('Administer my module'),
    // 'description' =&gt; t('Perform administration tasks for my module.'),
    // ),
    'access multi-steps forms' =&gt; array(
      'title' =&gt; t('Access multi-steps forms'),
      'description' =&gt; t('Enable the user to fill-in custom multi-steps forms'),
    ),
  );
}

/**
* Implements hook_menu().
*/
function multi_etapes_menu() {

  $items['myform'] = array(
    'title' =&gt; t('My multi-steps form'),
    'page callback' =&gt; 'multi_etapes_myform_page',
    'access arguments' =&gt; array('access multi-steps forms'),
    'type' =&gt; MENU_NORMAL_ITEM,
    'file' =&gt; 'inc/multi_etapes.pages.inc',
  );

  return $items;
}

/**
* Implements hook_STORE_KEY_mforms_init().
*/
function multi_etapes_myform_mforms_init() {
  $store = MformsSessionStore::getInstance('myform');
  $steps = MformsSteps::getInstance($store);

  // If using mustistep controls you need to explicitly define form steps and
  // its attributes.
  $controls = MformsMultiStepControls::getInstance($store, $steps, array(
    '_myform_step1' =&gt; array('value' =&gt; t('Bio'), 'weight' =&gt; -103),
    '_myform_step2' =&gt; array('value' =&gt; t('Hobbies'), 'weight' =&gt; -102),
    '_myform_step3' =&gt; array('value' =&gt; t('Summary'), 'weight' =&gt; -101)
  ));
  // Ajaxify the form stepping process.
  $controls-&gt;ajaxify();

  // Init the mforms.
  mforms_init_module('multi_etapes', 'myform', $store, $controls);
}

Qu'avons-nous fait ?

  1. On créé un droit pour accéder au formulaire multi-étape,
  2. On défini une entrée de menu "classique" pour accéder à ce formulaire (protégée par le droit créé précédemment),
  3. On indique que la fonction de callback "multi_etapes_myform_page" se situe dans le fichier /inc/ du module,
  4. On initialise notre formulaire multi-étapes où myform est la clef via le hook hook_STORE_KEY_mforms_init(),
  5. On défini le nombre d'étapes à l'intérieur du hook et on leur donne un nom (attention au poids),
  6. On fini d'initialiser le formulaire.

inc/multi_etapes.pages.inc

&lt;?php

/**
* Entry page for the multi-step form.
*
* @return array
*   Drupal renderable array.
*/
function multi_etapes_myform_page() {

  // Add user name to steps
  global $user;
  $uname = isset($user-&gt;name)?$user-&gt;name:'Guest';
 
  // Create parameters to be passed to the multi-step form
  $params = array('uname' =&gt; $uname);

  // Return Drupal renderable array.
  return array(
    'mform' =&gt; drupal_get_form('multi_etapes_myform', $params),
  );
}

/**
* Callback function to generate form.
*
* @param array $form
*   Drupal form array.
* @param array $form_state
*   Drupal form_state array.
* @param array $params
*   Optional params passed into form.
*
* @return array
*   Drupal form array.
*/
function multi_etapes_myform($form, &amp;$form_state, $params) {
  // pass defined parameters to mforms_build
  return mforms_build('myform', '_myform_step1', $form_state, $params);
}

/**
* Callback function to validate form inputs
*
* @param array $form
*   Drupal form array.
* @param array $form_state
*   Drupal form_state array.
*/
function multi_etapes_myform_validate($form, &amp;$form_state) {
  mforms_validate('myform', $form, $form_state);
}

/**
* Callback function to process the form inputs.
*
* @param array $form
*   Drupal form array.
* @param array $form_state
*   Drupal form_state array.
*/
function multi_etapes_myform_submit($form, &amp;$form_state) {
  mforms_submit('myform', $form, $form_state);
}

Encore beaucoup d'initialisations dans ce fichier :

  1. On défini la fonction appelée par notre menu,
  2. On récupère le nom de l'utilisateur courant pour montrer le passage de variable à notre formulaire,
  3. On appelle le formulaire via drupal_get_form,
  4. Notre formulaire multi-étapes est défini de façon traditionnelle avec les hook_form , hook_validate et hook_submit.

Il nous reste à mettre en place le code structurant chacune des étapes de notre formulaire.
Le module mform va chercher les étapes ( définies par _myform_stepX() ) dans un fichier qui doit être nommé de la façon suivante : nom_du_module.cleformulaire.inc et placé dans un dossier mforms à la racine de votre module.

Le coeur du fomulaire multi-étapes

Nous allons faire un formulaire d'exemple en 3 étapes :

  1. Informations sur l'utilisateur avec son login récupéré automatiquement, 
  2. Ses activités (Films, livres et sports),
  3. Un résumé des valeurs saisies, un champ pour envoyer un message à l'équipe du site et une case a cocher pour valider et terminer le formulaire.

Ce formulaire reprend les éléments du formulaire d'exemple fourni par le module mforms, en ajoutant des variations qui me semblent intéressantes.

mforms/multi_etapes.myform.inc - Première étape

/**
* First step called by the Mforms state machine.
*
* @param array $form_state
*   Drupal form_state array.
* @param string $next_step
*   Mforms next step callback name.
* @param mixed $params
*   Optional params passed into form.
*
* @return array
*   Drupal form array.
*/
function _myform_step1(&amp;$form_state, &amp;$next_step, $params) {
  // Define following step callback. If none set, that implies it is
  // the last step.
  $next_step = '_myform_step2';
  // Retrieve submitted values. This comes in handy when back action
  // occured and we need to display values that were originaly submitted.
  $data = mforms_get_vals('myform');
  // If we have the data it means we arrived here from back action, so show
  // them in form as default vals.
  if (!empty($data)) {
    $vals = $data;
  }
  elseif (isset($form_state['values'])) {
    $vals = $form_state['values'];
  }
  // Define form array and return it.
  $form = array();
  $form['login'] = array(
    '#type' =&gt; 'textfield',
    '#disabled'=&gt;true,
    '#title' =&gt; t('Login'),
    '#default_value' =&gt; isset($vals['loginv']) ? $vals['loginv'] : $params['uname'], // get login name from previous page (not submited but for design purpose).
  );
  $form['name'] = array(
    '#type' =&gt; 'textfield',
    '#title' =&gt; t('Name'),
    '#default_value' =&gt; isset($vals['name']) ? $vals['name'] : NULL,
  );
  $form['email'] = array(
    '#type' =&gt; 'textfield',
    '#title' =&gt; t('Email'),
    '#default_value' =&gt; isset($vals['email']) ? $vals['email'] : NULL,
    '#required' =&gt; TRUE,
  );
  $form['www'] = array(
    '#type' =&gt; 'textfield',
    '#title' =&gt; t('Your web site'),
    '#default_value' =&gt; isset($vals['www']) ? $vals['www'] : NULL,
  );
  // store the login in a hidden field so that the value is submited
  $form['loginv'] = array(
    '#type' =&gt; 'hidden',
    '#default_value' =&gt; isset($vals['loginv']) ? $vals['loginv'] : $params['uname'],
  );
  return $form;
}
/**
* Validate callback - validates email address.
*
* @param array $form
*   Drupal form array.
* @param array $form_state
*   Drupal form_state array.
*/
function _myform_step1_validate($form, &amp;$form_state) {
  if (!valid_email_address($form_state['values']['email'])) {
    form_set_error('email', t('Invalid email.'));
  }
}
  1. On défini la fonction de première étape,
  2. On indique l'étape suivante,
  3. On défini les champs à afficher tout en peuplant les valeurs présaisies si elles existent,
    Pour cet exemple, le champ "Login" est récupéré automatiquement. Si l'utilisateur est anonyme, "Guest" est affiché,
  4. On soumet la première étape du fomulaire à la fonction de validation.

mforms/multi_etapes.myform.inc - Deuxième étape

/**
* Step two.
*
* @param array $form_state
*   Drupal form_state array.
* @param string $next_step
*   Mforms next step callback name.
* @param mixed $params
*   Optional params passed into form.
*
* @return array
*   Drupal form array.
*/
function _myform_step2(&amp;$form_state, &amp;$next_step, $params) {
  $next_step = '_myform_step3';
  $form = array();

  $data = mforms_get_vals('myform');

  if (!empty($data)) {
    $vals = $data;
  }
  elseif (isset($form_state['values'])) {
    $vals = $form_state['values'];
  }

  $form['movies'] = array(
    '#type' =&gt; 'textarea',
    '#title' =&gt; t('Movies'),
    '#default_value' =&gt; isset($vals['movies']) ? $vals['movies'] : NULL,
  );
  $form['books'] = array(
    '#type' =&gt; 'textarea',
    '#title' =&gt; t('Books'),
    '#default_value' =&gt; isset($vals['books']) ? $vals['books'] : NULL,
  );
  $form['sports'] = array(
    '#type' =&gt; 'textarea',
    '#title' =&gt; t('Sports'),
    '#default_value' =&gt; isset($vals['sports']) ? $vals['sports'] : NULL,
  );

  return $form;
}

Rien de plus par rapport à l'étape 1, on passe à la dernière étape de ce formulaire.

mforms/multi_etapes.myform.inc - Troisième étape

/**
* Third step.
*
* @param array $form_state
*   Drupal form_state array.
* @param string $next_step
*   Mforms next step callback name.
* @param mixed $params
*   Optional params passed into form.
*
* @return array
*   Drupal form array.
*/
function _myform_step3(&amp;$form_state, &amp;$next_step, $params) {
  $form = array();
  // Get the collected values submited at each step.
  // Here is one difference - the second parameter that defines the step
  // from which we want to retrieve the data.
  $vals1 = mforms_get_vals('myform', '_myform_step1');
  $vals2 = mforms_get_vals('myform', '_myform_step2');
  $form['summary'] = array(
    '#type' =&gt; 'fieldset',
    '#title' =&gt; t('Summary'),
  );
  $form['summary']['sum_bio'] = array(
    '#markup'=&gt;
    "&lt;h3&gt;".t('Bio')."&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;".t('Login')." :&lt;/em&gt; ${vals1['loginv']}&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;".t('Name')." :&lt;/em&gt; ${vals1['name']}&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;".t('e-mail')." :&lt;/em&gt; ${vals1['email']}&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;".t('Website')." :&lt;/em&gt; ${vals1['www']}&lt;/li&gt;
     &lt;/ul&gt;",
  );
  $form['summary']['sum_hobbies'] = array(
    '#markup'=&gt;
    "&lt;h3&gt;".t('Hobbies')."&lt;/h3&gt;
    &lt;ul&gt;
      &lt;li&gt;&lt;em&gt;".t('Movies')." :&lt;/em&gt; ${vals2['movies']}&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;".t('Books')." :&lt;/em&gt; ${vals2['books']}&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;".t('Sports')." :&lt;/em&gt; ${vals2['sports']}&lt;/li&gt;
     &lt;/ul&gt;",
  );
  $data = mforms_get_vals('myform');
  if (!empty($data)) {
    $vals = $data;
  }
  elseif (isset($form_state['values'])) {
    $vals = $form_state['values'];
  }
  $form['message_team'] = array(
    '#type' =&gt; 'textarea',
    '#title' =&gt; t('A message for the team'),
    '#default_value' =&gt; isset($vals['message_team']) ? $vals['message_team'] : NULL,
  );
  $form['confirm']=array(
    '#type' =&gt; 'checkbox',
    '#title' =&gt; t('Validate and end the survey'),
  );
  return $form;
}
/**
* Validate callback.
*
* @param array $form
*   Drupal form array.
* @param array $form_state
*   Drupal form_state array.
*/
function _myform_step3_validate($form, &amp;$form_state) {
  if (!$form_state['values']['confirm']) {
    form_set_error('confirm', t('You have to validate the survey in order to continue !'));
  }
}
/**
* Implement submit callback for the last step to process all data submitted.
*
* @param array $form
*   Drupal form array.
* @param array $form_state
*   Drupal form_state array.
*/
function _myform_step3_submit($form, &amp;$form_state) {
  // Here do what you want with the data
   multi_etapes_save_data() // exemple function   // Send a mail to the team if there is a custom message   if($form_state['values']['message_team'] &amp;&amp; !empty($form_state['values']['message_team'])){ // add your own mail function     if(!_multi_etapes_drupal_mail('contact@votresite.com','webmaster@votresite.com',t('Answer to the survey'),$form_state['values']['message_team'])){       drupal_set_message(t('Something went wrong'),'error');     }   }
  // Call mforms_clean();
  // Clear all data from the session variable

  mforms_clean('myform');
  drupal_set_message(t('Thank you for your time.'));
}
  1. Cette fois on affiche un résumé des information saisies précédemment, on utilise la fonction mforms_get_vals avec un second paramètre pour récupérer les valeurs d'une étape en particulier,
  2. On affiche les résultats des deux étapes précédentes,
  3. On affiche un champ pour que l'utilisateur puisse envoyer un message à l'équipe du site,
  4. On met en place une case à cocher pour valider nos résultats et envoyer le message (si saisi),
  5. Une fonction de validation vérifie que la case a bien été cochée lors de l'envoi du formulaire,
  6. La fonction d'envoi traite les données (je ne développe pas ce point, mais on peut imaginer que les données saisies viennent peupler une node ou une entitée personnalisée),
  7. Si l'utilisateur a entré un message à destination de l'équipe on envoi ce message par mail (par exemple),
  8. On vide le contenu de notre formulaire (effacement de la variable de SESSION) et on affiche un message à l'utilisateur.

Et voilà ! Notre module est en place et fonctionnel. Les possibilités sont nombreuses et le système robuste permet de créer n'importe quel type de formulaire multi-étape.

Pour aller plus loin

Quelques astuces complémentaires.

Execution de javascript

Lors de la réalisation du formulaire multi-étape pour le site applicatif de l'eGrid, j'ai eu besoin d'executer du code javascript pour réaliser un carrousel (étape Évaluation).

Le problème avec l'execution de javascript dans les appels en ajax Drupal, c'est qu'il est toujours délicat de savoir à quel moment se "brancher" pour executer son code. Le morceau de javascript suivant permet d'executer le code souhaité à l'étape désirée. Attention cependant le code est réinterprété a chaque reconstruction du DOM (et donc à chaque affichage de message d'erreur de validation).

J'ai placé le code dans js/multi_etapes.js et modifié multi_etapes.info pour déclarer le fichier js ( scripts[] = js/multi_etapes.js ).

(function ($, Drupal, window, document, undefined) {

  Drupal.behaviors.multi_etapes = {
    attach: function(context, settings) {

      if(context === document || context[0].nodeName == 'FORM'){
        var step = $('[id^="edit-steps-btns-wrapper"] .current-step').attr('id');
         
        // si on est bien sur un formulaire multi-étapes
        if(step){
          var regex = /step(\d)/;
          var match = regex.exec(step);
          step = parseInt(match[1]);

          alert('Etape ' + step);
          // on peut envisager un switch pour gérer chaque étape.
        }
      }
    }
  }
})(jQuery, Drupal, this, this.document);

Si vous vous aventurez dans la réalisation d'un tel formulaire avec javascript, je serai très interessé par vos retours et vos solutions pour bien gérer l'execution JS.

Accéder aux données de la session

Le fait de travailler avec les sessions permet d'accéder à tout moment aux valeurs du formulaire. Ceci m'a été particulièrement utile lors de la génération d'une fiche de résumé des saisies en PDF. Pour accéder aux valeurs stockées :

$valeurs = $_SESSION['clefduformulaire']

Ce sera tout pour aujourd'hui concernant les formulaires multi-étapes sous Drupal, il existe d'autres méthodes utilisant le module webform ou en construisant directement avec l'API form de Drupal, avez-vous essayé ces autres méthodes ? Qu'en pensez-vous ?

Pages