Planète

Par Mantalo Conseil
Benjamin Grapeloux
Agence web, Agence de Communication et Marketing en Dordogne (Aquitaine)

Nouveau site internet : Écopôle Aquitaine

Une architecture innovante signée Ducan Lewis

Construire avec la nature est l'axe majeur des travaux de l'architecte et designer Ducan Lewis. Le projet Écopôle Périgord Aquitaine est un très beau projet qui s'inscrit totalement dans cet esprit. C'est dans ce contexte très privilégié que de jeunes entreprises pourront très prochainement amorcer leur activité et partager un espace de travail de grande qualité.

Un socle Drupal pour un site évolutif

Le site a pour vocation de présenter l'ensemble des installations et des services de la zone Écopôle Périgord Aquitaine établie à Vélines.

Le public attendu se compose dans un premier temps, de créateurs d'entreprises et de porteurs de projets professionnels en lien avec le développement durable, et à la recherche d'une pépinière d'entreprises ou d'un incubateur de projets. Il trouveront à Écopôle Périgord Aquitaine des installations pour les accueillir, mais également pour les accompagner dans le développement de leur activité.

Un peu plus tard, le site internet Écopôle Périgord Aquitaine  aura pour vocation de promouvoir le savoir faire des entreprises hébergées sur la zone d'activité. Il sera alors question de développer l'arborescence du site telle que nécessaire pour publier un contenu plus étoffé et diversifié. Pourquoi ne pas aller jusqu'à mettre à la disposition de chaque entreprise de la zone un espace de valorisation de son activité ? En tout cas, l'architecture du site le permettra !

Un site de qualité pour un projet de qualité

À la mesure du concept et de son intérêt économique et environnemental, le site internet de l'Écopôle Périgord Aquitaine  a été développé selon un cahier de spécifications très qualitatif :

  • Le contenu est accessible (conforme au référentiel WCAG), donc aisément consultable par tous publics, y compris les personnes handicapées, quelque soit la nature de leur handicap,
  • Toutes les pages du site sont facilement lisibles à partir de n'importe quelle taille d'écran fixe ou mobile (Responsive Web Design),
  • Le code HTML est valide conformément au référentiel W3C, ce qui expose dans d'excellentes conditions son potentiel de référencement naturel.

Un univers graphique cohérent et harmonieux

Tous les visuels du site ont été réalisés dans l'esprit de l'univers graphique piloté par le logo Écopôle Périgord Aquitaine également conçu et créé par l'agence Mantalo Conseil. L'univers apaisant et serein des pages du site se veulent le reflet de ce que sera ce lieu de travail et de production totalement respectueux des hommes et de leur environnement. Pour autant, cet ensemble "soft" n'empêche pas des contrastes de couleurs intégralement compatibles avec les normes d'accessibilité des sites internet.

 

Vous aussi êtes adeptes d'un internet de qualité? Contactons-nous et échangeons sans tarder pour étudier comment communiquer soft et efficace à la fois !

Par AlanT
Alan Tondelier

Générer et personnaliser ses PDF avec Drupal et dompdf

Pour générer des fichiers PDF des pages Drupal, il existe le module "Print", qui contient le sous module "print_pdf". Ce module permet de convertir très simplement en PDF toute page possédant une URL interne. Print propose même un système de templates permettant de peaufiner l'affichage des PDF pour chaque type de nodes.

Cependant, il arrive d'avoir besoin de générer des PDF dont l'affichage diffère complètement de la page drupal associée et où des données non présentes sur la page doivent être récupérées. Cet article explique comment parvenir a ses fins en détournant un petit peu le module print. Cet article s'appuie sur la version 2.x du module.

Quelle librairie PDF choisir ?

Présentation

Lorsque vous installez le module print et que vous activez la génération de pdf (module print_pdf) il vous est demandé de déposer une librairie PDF dans votre Drupal. Il existe à ce jour 4 librairies PDF supportées par print pour générer vos PDF :

  1. dompdf
  2. TCPDF
  3. mPDF (avec la version 2.x de print)
  4. wkhtmltopdf

Chaque librairie possède ses avantages/inconvénients et leur choix va dépendre énormément de votre projet. Une étude comparative de ces 4 libraries est disponible sur fuseinteractive.ca . 

Personnellement, j'ai trouvé TCPDF trop "mauvais" dans la gestion des CSS. La mise en place de whtmltopdf délicate car on n'a pas toujours l'accès root du serveur qui héberge le site, même si c'est la solution la plus puissante (et la seule vraiment valable pour vos très gros pdf).

Concernant dompdf et mPDF, pour généraliser, dompdf a un meilleur support CSS que mPDF mais est moins performant que ce dernier.

J'ai eu l'occasion de pousser dompdf assez loin pour générer des pdf plutôt complexes : http://egrid.epg-project.eu/fr/egrid : 10 langues avec différents jeux de caractères, la possibilité d'ajouter un glossaire dans 4 langue de son choix, une numérotation des pages et un footer personnalisé.

Génération de PDF complexe avec Drupal
Extrat d'un PDF dynamique complexe avec Drupal avec footer personnalisé et numérotation de pages 

Pour la suite de cet article, j'utiliserai la bibliothèque dompdf pour générer mes fichiers.

Dompdf

Installer la librairie dompdf

Pour installer simplement la librairie dompdf, il vous faut :

  1. Récupérer la dernière version de dompdf : https://github.com/dompdf/dompdf/tags,
  2. Récupérer php-font-lib 0.2.2 : https://github.com/PhenX/php-font-lib/tree/0.2.2,
  3. Si vous ne l'avez pas, le module libraries,
  4. Dézipper dompdf dans sites/all/libraries/dompdf,
  5. Dézipper php-font-lib dans sites/all/libraries/dompdf/lib/php-font-lib,
  6. v2.0 du module : veillez à activer également le module "dompdf library handler".

Dompdf est installé, vous pouvez peaufiner vos réglages dans <admin>/config/user-interface/print .

Générer et personnaliser des PDF "sur-mesure" avec Dompdf

Ajouter des templates à n'importe quelle page possédant un lien interne

Le module print permet la personnalisation de templates selon le modèle hiérarchique suivant ( 1 sera prioritaire sur 2 qui sera prioritaire sur 3, etc...) : 

  1. print--[format]--node--[type]--[nid].tpl.php,
  2. print--[format]--node--[type].tpl.php,
  3. print--[format].tpl.php,
  4. print--node--[type].tpl.php,
  5. print.tpl.php dans votre thème,
  6. print.tpl.php dans le module print (par défaut).

[format] est le format de sortie du module print (html, pdf ou mail), [type] est le nom interne du type de contenu, [nid] est l'ID du node.

Cependant, cette structure ne permet pas d'appliquer un template à n'importe quelle page de notre site. Par exemple, comment faire pour ajouter un template PDF personnalisé pour une page générée par views ? Sans pour autant écraser le template général ?

Pour remédier à cela, vous pouvez utiliser hook_preprocess_print() :

template.php de votre theme

/**
 * Implements hook_preprocess_print().
 */

function demo_preprocess_print(&$vars){

  // add support for template files
  // print--[format]--path-[drupal-path].tpl.php
  // and
  // print--path-[drupal-path].tpl.php
  // with recursive path suggestions

  $cpath = current_path();
  $crumbs = explode('/',$cpath);

  // if first crumb is "print" remove it

  if($crumbs[0] == "print" ){ array_shift($crumbs); }

  $vars['theme_hook_suggestions'][] =  "print__path_${crumbs[0]}";
  $vars['theme_hook_suggestions'][] =  "print__${vars['format']}__path_${crumbs[0]}";

  // if there is more than one crumb
  if(count($crumbs) > 1){

    // remove crumb already used 
    array_shift($crumbs);

    foreach ($crumbs as $key => $crumb) {

      // get number of current theme suggestions
      $size = count($vars['theme_hook_suggestions']);

      // add crumb to theme suggestions
      $vars['theme_hook_suggestions'][] = $vars['theme_hook_suggestions'][$size-2]."_${crumbs[$key]}";
      $vars['theme_hook_suggestions'][] = $vars['theme_hook_suggestions'][$size-1]."_${crumbs[$key]}";

    }

  }

}

Ainsi, grâce a cette fonction, je peux facilement mettre en place un template pour personnaliser la sortie de mon pdf.

Si le chemin de ma vue est "mavue", le template PDF sera : print--pdf--path-mavue.tpl.php ou print--path-mavue.tpl.php (pour personnaliser à la fois le PDF et l'affichage pour impression).

Plus intéressant la fonction gère également les chemins à plusieurs arguments :
Par exemple si mon chemin est mavue/arg2, le template PDF pourra être (du plus selectif au moins selectif) :

  1. print--pdf--path-mavue-arg2.tpl.php,
  2. print--path-mavue-arg2.tpl.php,
  3. print--pdf--path-mavue.tpl.php,
  4. print--path-mavue.tpl.php

Ajouter un footer personnalisé à vos PDF et les numéros de pages...

Pour ajouter des informations en pied de page de vos PDF, vous devez tout d'abord activer l'utilisation de PHP dans dompdf. 
Rendez vous dans libraries/dompdf/dompdf_config.custom.inc.php et décommentez la ligne 12

define("DOMPDF_ENABLE_PHP", true);

Maintenant dans le template qui vous intéresse, rajoutez le code suivant après l'ouverture de la balise <body>

    <script type="text/php">
      if (isset($pdf)) {
        $font = Font_Metrics::get_font("verdana");;
        $size = 10;
        $color = array(0,0,0);
        $text_height = Font_Metrics::get_font_height($font, $size);

        $w = $pdf->get_width();
        $h = $pdf->get_height();

        $footer = $pdf->open_object();

        // Draw a line along the bottom
        $y = $h - 25;
        $pdf->line(15, $y, $w - 15, $y, $color, 1);

        $y += $text_height / 2;
        $pdf->page_text(15, $y, 'Texte de mon footer', $font, $size, $color);

        $pdf->close_object();
        $pdf->add_object($footer, "all");

        // Center the text
        $width = Font_Metrics::get_text_width("Page 1 of 2", $font, $size);
        $pagenumtxt = t("Page !n of !total", array("!n" => "{PAGE_NUM}", "!total" => "{PAGE_COUNT}"));
        $pdf->page_text($w - 15 - $width, $y, $pagenumtxt, $font, $size, $color);
      }
    </script>

Le code ci-dessous rajoutera une ligne, le texte "Texte de mon footer" et la numérotation des pages à droite. A vous d'adapter ce code pour coller à vos besoins. Pour plus d'options de mises en page, vous pouvez vous pencher sur l'API de dompdf .

J'espère que cet article vous fera gagner du temps lors de la mise en place de PDF avancés, il y aurait encore beaucoup de choses à dire sur le sujet, aussi si vous avez des remarques n'hésitez pas à les donner en commentaires !

Par Marc Delnatte
Akabia

Drupal vs Joomla - Quel CMS choisir pour son site internet ?

Choisir son CMS est important avant de commencer à développer son site internet. En effet, une fois choisi, il est très compliqué et coûteux de vouloir changer de CMS en cours de projet. Chaque CMS a sa propre architecture que ce soit Drupal ou Joomla.

Dans cet article nous allons comparer deux CMS (Joomla et Drupal) que l'agence Akabia a utilisé pour la réalisation de sites internet.







Par benftwc

Drupal Multisite et Travail collaboratif

Pourquoi le Multisite ?

Pour vous permettre de travailler en équipe sur un même projet, sans parler de CVS, Drupal a pensé au fichier sites.php, dédié aux « Multi-sites », soit, plusieurs sites au sein d’une seule instance de Drupal.

Drupal Multisite

Drupal Multisite

Le Drupal Multisite est généralement utilisé pour séparer plusieurs univers (tels que membre.site.fr et admin.site.fr), mais également plusieurs environnements techniques (Windows – Linux, Apache – NGinX).

Structure et mise en place du Drupal Multisite

Le mettre en place est extrêmement simple :

<?php
  $sites['subdomain.domain.tld'] = 'environnement';
?>

Pour reprendre la structure ci-dessus, la clé de $sites correspond à l’url et la valeur correspond au répertoire de configuration de l’environnement. (dans notre exemple, ROOT/sites/environnement).

Mise en place dynamique

Pour aller plus loin, et nous permettre de baser notre environnement en fonction du développeur, nous allons utiliser une variable d’environnement qui servira à séparer chaque base de donnée, pour chacun de vos développeurs / intervenants.

Cette variable est à renseigner dans le VHOST pointant vers votre instance Drupal

SetEnv APPLICATION_ENV dev-username

Avec ici autant d’utilisateurs que vous voudrez, en gardant 1 environnement / Vhost / URL !

Pensez également à versionner les config, cela évitera en cas de soucis de perdre toutes vos configurations !

<?php

// sites/sites.php
$env = getenv('APPLICATION_ENV');
if (!empty($env)) {
    $sites[$_SERVER['SERVER_NAME']] = $env;
}
?>

En sauvegardant ce code dans votre sites.php, vous serez capable de travailler à plusieurs développeurs sur une même instance de Drupal. Pensez également à synchroniser de temps en temps vos bases de données si nécessaire, cette technique n’est pas « viable » lorsque vous travaillerez avec trop de personnes.

Aller plus loin

Dans ce cas, il faudra prévoir de passer par des méthodes plus « Drupal », soit, par le biais de hook_update, d’export Features-Strongarm, ou de modules Custom ne nécessitant que très peu de configuration.

The post Drupal Multisite et Travail collaboratif appeared first on Benftwc.

Par benftwc

Drupal et le Responsive Design

Drupal responsive design

Drupal responsive design

Avant tout, sur Drupal, le Responsive Web Design, c’est quoi / pour qui ?

Avec le nouvel essors des plateformes mobiles et la naissance des webapps, il est aujourd’hui devenu primordial à toute société de posséder son propre site « responsive webdesign » (que j’abrégerais RWD), c’est à dire, un site adaptatif, calculé et affiché différemment en fonction du support (mobile, tablette, ordinateur, téléviseurs). Cependant, si cette fonctionnalité n’est pas pensée au début, il peut vite devenir fastidieux de le mettre en place.

Je distingue 2 grandes réponses au « problème » du RWD : La mise en place d’un template « full RWD » et la mise en place d’un thème dedié.

Template Full RWD

Cette solution est la plus simple car tout se passe au niveu de votre intégration, soit, HTML + CSS + JavaSript. Cette solution ne touchant pas à Drupal, et étant traité par Marie Guillaumet (Refonte de mon portfolio partie 1 et partie 2), je n’en parlerais pas plus ici.

Le thème dedié

Le thème dedié est plus pratique dans certains cas, notament quand le site est déjà sorti, avec toute une intégration de prévue et pas de temps pour le refaire. Néanmoins, cela impliquera autant de modifications et de temps de maintient que de thèmes dédiés.

La technique est simple ici, il vous suffit d’utiliser le module Mobile Tools vous allez pouvoir détecter quel est le support du visiteur, et lui fournir un thème différent en fonction du retour.

De cette manière, vous aurez par exemple desktop_mondesign et mobile_mondesign, les deux adaptés au contenu (soit PC pour desktop, smartphone/iphone pour mobile).

De plus, le module embarque des options de configuration pour CCK, Display Suite, Panels … En soit, la méthode est plus compliquée à mettre en place, mais permet une réelle séparation des fichiers, ce qui n’est pas le cas de la première  méthode.

The post Drupal et le Responsive Design appeared first on Benftwc.

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

<?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' =>  array(
    // 'title' => t('Administer my module'),
    // 'description' => t('Perform administration tasks for my module.'),
    // ),
    'access multi-steps forms' => array(
      'title' => t('Access multi-steps forms'),
      'description' => t('Enable the user to fill-in custom multi-steps forms'),
    ),
  );
}

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

  $items['myform'] = array(
    'title' => t('My multi-steps form'),
    'page callback' => 'multi_etapes_myform_page',
    'access arguments' => array('access multi-steps forms'),
    'type' => MENU_NORMAL_ITEM,
    'file' => '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' => array('value' => t('Bio'), 'weight' => -103),
    '_myform_step2' => array('value' => t('Hobbies'), 'weight' => -102),
    '_myform_step3' => array('value' => t('Summary'), 'weight' => -101)
  ));
  // Ajaxify the form stepping process.
  $controls->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

<?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->name)?$user->name:'Guest';
  
  // Create parameters to be passed to the multi-step form
  $params = array('uname' => $uname);

  // Return Drupal renderable array.
  return array(
    'mform' => 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, &$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, &$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, &$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(&$form_state, &$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' => 'textfield',
    '#disabled'=>true,
    '#title' => t('Login'),
    '#default_value' => isset($vals['loginv']) ? $vals['loginv'] : $params['uname'], // get login name from previous page (not submited but for design purpose).
  );

  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#default_value' => isset($vals['name']) ? $vals['name'] : NULL,
  );
  $form['email'] = array(
    '#type' => 'textfield',
    '#title' => t('Email'),
    '#default_value' => isset($vals['email']) ? $vals['email'] : NULL,
    '#required' => TRUE,
  );
  $form['www'] = array(
    '#type' => 'textfield',
    '#title' => t('Your web site'),
    '#default_value' => isset($vals['www']) ? $vals['www'] : NULL,
  );

  // store the login in a hidden field so that the value is submited

  $form['loginv'] = array(
    '#type' => 'hidden',
    '#default_value' => 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, &$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(&$form_state, &$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' => 'textarea',
    '#title' => t('Movies'),
    '#default_value' => isset($vals['movies']) ? $vals['movies'] : NULL,
  );
  $form['books'] = array(
    '#type' => 'textarea',
    '#title' => t('Books'),
    '#default_value' => isset($vals['books']) ? $vals['books'] : NULL,
  );
  $form['sports'] = array(
    '#type' => 'textarea',
    '#title' => t('Sports'),
    '#default_value' => 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(&$form_state, &$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' => 'fieldset',
    '#title' => t('Summary'),
  );

  $form['summary']['sum_bio'] = array(
    '#markup'=>
    "<h3>".t('Bio')."</h3>
    <ul>
      <li><em>".t('Login')." :</em> ${vals1['loginv']}</li>
      <li><em>".t('Name')." :</em> ${vals1['name']}</li>
      <li><em>".t('e-mail')." :</em> ${vals1['email']}</li>
      <li><em>".t('Website')." :</em> ${vals1['www']}</li>
     </ul>",
  );

  $form['summary']['sum_hobbies'] = array(
    '#markup'=>
    "<h3>".t('Hobbies')."</h3>
    <ul>
      <li><em>".t('Movies')." :</em> ${vals2['movies']}</li>
      <li><em>".t('Books')." :</em> ${vals2['books']}</li>
      <li><em>".t('Sports')." :</em> ${vals2['sports']}</li>
     </ul>",
  );



  $data = mforms_get_vals('myform');

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

  $form['message_team'] = array(
    '#type' => 'textarea',
    '#title' => t('A message for the team'),
    '#default_value' => isset($vals['message_team']) ? $vals['message_team'] : NULL,
  );

  $form['confirm']=array(
    '#type' => 'checkbox',
    '#title' => 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, &$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, &$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'] && !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');

          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 ?

Par AlanT
Alan Tondelier

Installer et personnaliser le module CKEditor pour Drupal 7

Installer et personnaliser le module CKEditor pour Drupal 7

La mise en place du module ckeditor pour Drupal peut s'avérer plus compliquée que prévue. Ce rapide guide liste les étapes à suivre pour installer sans encombres le module. Une seconde partie s'intéresse à l'installation de plugins complémentaire étendant les fonctionnalités de cet éditeur wysiwyg de qualité.

Ajouter ckeditor à votre Drupal

Récupération des fichiers

Tout commence par une petite commande drush bien sentie. Si vous ne connaissez pas drush, faites vrombir votre curseur au plus vite pour le récupérer, cet utilitaire en ligne de commandes est un indispensable du développement Drupal (j'y reviendrai). Attention les commandes ci-dessous ne fonctionnent qu'avec les dernières versions de drush, pour les plus anciennes vous devez d'abord télécharger le module (avec le switch "dl").

drush en ckeditor -y

Je vous conseille également de récupérer ckeditor_link qui vous permettra de créér des hyperliens vers vos contenus Drupal directement via l'interface de Ckeditor, indispensable !

drush en ckeditor_link -y

Une fois la récupération des modules faite, rendez vous sur http://ckeditor.com/download pour télécharger la librairie ckeditor.

Une petite personnalisation SEO-friendly

Afin de fournir du code plus moderne, apprécié des moteurs de recherches mais également pour fournir plus d'accessibilité à vos contenus, j'ai l'habitude d'effectuer une petite personnalisation de ckeditor avant de le télécharger : ajouter le support de la balise <figure> et <figcaption>.

Sur la page de ckeditor, prenez l'option "Or let met customize CKEditor" puis le bouton rouge.
Prendez le preset "Full" puis dans la partie basse, colonne de droite trouvez "Enhanced Image" et basculez-le colonne de gauche.

Si vous suivez cette étape, les  images que vous chargerez via CKEditor pourront être enrichies de la sémantique HTML5.


Le plugin Enhanced Image ajoutera les balises html5 figure et figcaption.

Ajoutez ensuite toutes les langues à ckeditor (j'ai rencontré des problèmes en ne choisissant que les langues utilisées sur mes sites), et récupérez votre librairie.

Une fois votre CKEditor téléchargé, placez le contenu de l'archive dans votre dossier /sites/all/modules/contrib/ckeditor/ckeditor .

CKFinder pour gérer vos fichiers

CKEditor ne sait gérer seul l'upload de fichiers sur votre serveur, pourtant il est très utile de pouvoir ajouter des visuels ou attacher des fichiers directement dans le corps de votre texte.
Heureusement, il existe des plugins pour remédier à ce manque. 

Le plus connu et utilisé d'entre eux se nomme CKFinder (payant mais possède une version de démonstration avec quelques limitations pour un usage non commercial).

Pour l'ajouter à votre CKEditor, les étapes sont les suivantes :

  1. Récupérez le plugin ici,
  2. Drupal cherchera par défaut ckfinder dans le répertoire /sites/all/modules/contrib/ckeditor/ckfinder. Dézippez-le à cet emplacement.
  3. Pour que le système de droits de Drupal gère correctement les permissions de ckeditor, vous devez modifier le fichier config.php de CKFinder.

Supprimez ces lignes (21-34) :

function CheckAuthentication()
{
    // WARNING : DO NOT simply return "true". By doing so, you are allowing
    // "anyone" to upload and list the files in your server. You must implement
    // some kind of session validation here. Even something very simple as...

    // return isset($_SESSION['IsAuthorized']) && $_SESSION['IsAuthorized'];

    // ... where $_SESSION['IsAuthorized'] is set to "true" as soon as the
    // user logs in your system. To be able to use session variables don't
    // forget to add session_start() at the top of this file.

    return false;
}

Et remplacez les par 

require_once '../../../../includes/filemanager.config.php';
  1. Finissez par éditer la variable $cookie_domain de votre settings.php
 $cookie_domain = '.votrenomdedomain.ext';

Votre CKFinder est maintenant fonctionnel.

Configuration de CKEditor

Les bases

Par défaut CKEditor a créé deux profils à partir des formats de textes installés par défaut (lors d'une mise en route standard de Drupal). Nous allons voir brievement la configuration d'un de ces profils.

La configuration des profils se fait dans admin/config/content/ckeditor/.

Modifiez l'un des profil qui vous intéresse et dans "Apparence de l'éditeur", choisissez les boutons de barre d'outil à faire apparaitre pour ce profil. Si vous souhaitez de plus activer le CKEditor Link, cochez la case dans la sous rubrique Plugins ET rendez vous dans la configuration du format de texte correspondant pour activer le filtre CKEditor Link afin d'avoir de belles URLs.

Je vous conseille également de limiter les balises dans "Nettoyer et afficher" > "Formats de polices de caractères". Bien souvent la balise H1 est inutile car déjà présente pour le titre de la page, d'autres balises peuvent selon vos cas être inutiles ou source de confusion pour les utilisateurs, à vous de faire le tri.

N'oubliez pas non plus d'activer CKFinder dans les "paramètres de l'explorateur de fichiers".

Une fois ces réglages effectués, CKEditor devrait être visible dans vos zones de textes possédant un format de texte associé à un profil.

Configurations avancées

Très vite, il devient intéressant de pouvoir définir ses propres styles dans ckeditor. Pour ce faire, dans les réglages d'un profil, dans CSS puis styles prédéfinis, définissez le chemin de ckeditor.styles.js, j'ai l'habitude d'utiliser cette syntaxte "%tjs/ckeditor.styles.js" pour appeler le fichier dans le theme principal du site (et non celui de l'administration comme le suggère Drupal !).

L'appel dans le thème évite de supprimer le fichier lors d'une mise à jour du module ou de la librairie.

Vous trouverez la copie originale de ckeditor.style.js dans le répertoire du module CKEditor. Voici un extrait du ckeditor.styles.js que j'utilise sur ce blog :

/*
Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

/*
 * This file is used/requested by the 'Styles' button.
 * The 'Styles' button is not enabled by default in DrupalFull and DrupalFiltered toolbars.
 */
if(typeof(CKEDITOR) !== 'undefined') {
    CKEDITOR.addStylesSet( 'drupal',
    [

            /* Block Styles */

            // code wrapper

            {

                name: 'Code Wrapper',
                element: 'pre',

            },

            /* Inline Styles */

            // These are core styles available as toolbar buttons. You may opt enabling
            // some of them in the "Styles" drop-down list, removing them from the toolbar.

            { name : 'Bleu'  , element : 'span', attributes : { 'class' : 'bleu' } },
            { name : 'Orange'  , element : 'span', attributes : { 'class' : 'orange' } },

            { name : 'Big'        , element : 'big' },
            { name : 'Small'      , element : 'small' },

            // code

            {
                    name : 'Code PHP',
                    element : 'code',
                    attributes :
                    {
                            'class' : 'language-php',
                    }
            },

            {
                    name : 'Code CSS',
                    element : 'code',
                    attributes :
                    {
                            'class' : 'language-css',
                    }
            },

            {
                    name : 'Code HTML',
                    element : 'code',
                    attributes :
                    {
                            'class' : 'language-markup',
                    }
            },

            {
                    name : 'Code JS',
                    element : 'code',
                    attributes :
                    {
                            'class' : 'language-javascript',
                    }
            },

...etc

Étendre CKEditor

Ajout de plugins

La communauté de CKEditor a créé un certain nombre de plugins téléchargeables sur cette page : http://ckeditor.com/addons/plugins/all.

Ces plugins s'incorporent directement au ckeditor de votre Drupal en téléchargeant l'archive du plugin, et en l'extrayant deux fois, dans le dossier /modules/contrib/ckeditor/plugins et dans /modules/contrib/ckeditor/ckeditor/plugins. (En réalité il n'est pas nécessaire de tout extraire en double, mais sachez que cette méthode est "sûre").

Une fois vos plugins ajoutés, leur activation se fait dans "Apparence de l'éditeur" > "Plugins".

Création de plugin

Il est évidemment possible de créer son propre plugin CKEditor au sein même de Drupal, je reviendrai sur ce point lors d'un prochain billet.

 

Par AlanT
Alan Tondelier

Lancement du Blog !

Lancement du Blog !

Enfin !
Après plusieurs semaines de questionnement personnel ponctuées principalement par des "faut-il ?", le Blog est finalement en ligne.

Qu'attendre de ce blog ?

Ce blog est né de ma volonté de partager des connaissances acquises au cours des 3 dernières années sur Drupal.
En effet, au fil des semaines passées a approfondir le CMS Drupal, j'ai été à plusieurs reprises surpris de ne pas lire plus d'articles sur des problèmes récurrents, rencontrés par de nombreuses personnes de la communauté et qui ne trouvent que des réponses partielles, incomplètes ou "alambiquées".

Sans avoir la prétention d'apporter des réponses "ultimes" ou parfaites, je veux avant tout partager avec ceux qui me liront (coucou !) des pistes cohérentes, fonctionnelles et permettant, je l'espère, d'apporter une brique de qualité à l'édifice Drupal.

Le blog d'un autodidacte sur la scène Drupal & Web

Qui suis-je ?
Pour faire court, je suis un ingénieur diplômé de Grenoble-INP - Pagora filière communication imprimée. Ayant toujours côtoyé de près les sites Internet, depuis l'époque où couinaient les modems 56K, j'ai en 2011 entrepris de tenter l'aventure web, d'abord à Paris puis à Rouen en Haute Normandie.
Après près de 3 années en agence, j'ai finalement décidé de prendre mon indépendance.

Je ponctuerai ce blog de quelques articles plus personnels afin de donner quelques retours d'expérience sur cette aventure numérique.

Une promesse avec moi-même

Il est très facile de ne pas tenir la rédaction d'un blog, aussi j'essaierai dans la mesure du possible de poster un article au minimum par semaine afin de faire vivre cet espace :)

Blog ... GO !

 

Par AlanT
Alan Tondelier

Fournir un flux XML et RSS valide à partir d&#039;une vue Drupal

Il vous est peut-être arrivé de rencontrer des erreurs de validation de vos flux RSS, ou de tout flux XML générés avec des vues Drupal. Cet article propose des pistes de réponses pour assurer leur validation. A noter que je m'intéresse ici à une solution Drupal, cependant cette démarche est tout à fait adaptable à d'autres CMS/technologies.

Caractères interdits et sections CDATA

Une histoire de norme Unicode

Les flux XML doivent se contraindre à des standards établis par le w3c, parmis ces standards il existe une norme qui va nous intéresser tout particulièrement pour nos sites Drupal : celle concernant l'encodage des caractères de nos flux. Celle-ci précise que tous les caractères à l'intérieur de vos balises xml doivent se plier à la norme ISO/IEC 10646.

Il arrive que certain caractères saisis dans vos contenus, ne valident pas la norme ISO/IEC 10646. Ceci arrive notemment lorsque l'on créé des contenus à partir de copier/coller sauvages à partir d'un logiciel tier. Conséquence directe, notre flux ne passe pas la validation et une belle erreur est listée sur le validateur w3c :

Line 79, Column 68: non SGML character number 31

Nous allons voir comment chasser ces caractères indésirables.

Utilisation des sections CDATA pour assurer la compatibilité de vos flux

Une erreur classique dans la génération d'un flux xml est la présence de markup html dans les balises XML.
Pour éviter ce problème, Drupal converti en entitées HTML le contenu des champs lors de la génération de l'affichage de votre vue (et donc de votre flux). 

Cependant certaines visionneuses de flux ou logiciels d'importation peuvent avoir du mal à gérer les entitées HTML et se comportent mieux avec du code HTML "brut". Pour permettre l'inclusion de balises HTML dans notre XML, une solution toute simple est d'utiliser les sections CDATA :

  <item>
    <title>Titre de mon article</title>
    <link>http://blog.alantondelier.net/lien</link>
    <description>
      <![CDATA[ <h1>Ceci est un titre</h1> <img src="http://blog.alantondelier.net/uneimage.jpg" alt="Une image dans mon flux" /> <p>Blabla</p> ]]>
    </description>
  </item>

Couplées à la fonction html_entity_decode pour retrouver notre markup, les sections CDATA permettent d'assurer la compatibilité de nos flux XML tout en laissant le code valide.

Mise en place de la solution

Intéressons-nous à la mise en place d'une solution pour filtrer les mauvais caractères et pour ajouter les sections <[CDATA[]]>.

Nous allons intervenir au niveau du theming Drupal, et plus particulièrement au niveau du template de views générant le code pour chaque élément du flux.
Prenons l'exmple le plus courant : celui d'un flux RSS basique généré par views. Pour les autres cas la méthode présentée est à adapter selon vos besoins. 

Pour les flux RSS, le template appelé sera views-view-row-rss.tpl.php. Placez une copie de ce template dans votre theme et ouvrez le :

<?php

/**
 * @file
 * Default view template to display a item in an RSS feed.
 *
 * @ingroup views_templates
 */
?>
  <item>
    <title><?php print $title; ?></title>
    <link><?php print $link; ?></link>
    <description><?php print $description; ?></description>
    <?php print $item_elements; ?>
  </item>

Nous allons commencer par modifier le template pour décoder l'HTML et ajouter une section CDATA dans notre balise <description> (il est possible d'effectuer la même opération sur <title> si vous y affichez de l'html "brut").

    <description>
      <![CDATA[ <?php print html_entity_decode($description); ?> ]]>
    </description>

Il faut également retirer tous les caractères interdits par la norme XML. On utilise une fonction PHP de traitement de chaîne, empruntée ici, que l'on ajoute au template.php de notre theme :

/**
 * Removes invalid XML
 *
 * @access public
 * @param string $value
 * @return string
 */

function montheme_xmlcleaner($value)
{

    $ret = "";
    $current;
    if (empty($value))
    {
        return $ret;
    }

    $length = strlen($value);
    for ($i=0; $i < $length; $i++)
    {
        $current = ord($value{$i});
        if (($current == 0x9) ||
            ($current == 0xA) ||
            ($current == 0xD) ||
            (($current >= 0x20) && ($current <= 0xD7FF)) ||
            (($current >= 0xE000) && ($current <= 0xFFFD)) ||
            (($current >= 0x10000) && ($current <= 0x10FFFF)))
        {
            $ret .= chr($current);
        }
        else
        {
            $ret .= " ";
        }
    }
    return $ret;
}

On termine par ajouter l'appel à cette fonction dans notre template views-view-row-rss.tpl.php :

<?php

/**
 * @file
 * Default view template to display a item in an RSS feed.
 *
 * @ingroup views_templates
 */

?>
  <item>
    <title><?php print montheme_xmlcleaner($title); ?></title>
    <link><?php print $link; ?></link>
    <description>
      <![CDATA[ <?php print montheme_xmlcleaner(html_entity_decode($description)); ?> ]]>
    </description>
    <?php print $item_elements; ?>
  </item>

La magie opère : votre flux xml est valide. 

N'hésitez pas à partager vos expériences sur les flux xml & Drupal en commentaire ou si vous avez rencontré un problème différent sur le sujet.

Par AlanT
Alan Tondelier

Fournir un flux XML et RSS valide à partir d&#039;une vue Drupal

Il vous est peut-être arrivé de rencontrer des erreurs de validation de vos flux RSS, ou de tout flux XML générés avec des vues Drupal. Cet article propose des pistes de réponses pour assurer leur validation. A noter que je m'intéresse ici à une solution Drupal, cependant cette démarche est tout à fait adaptable à d'autres CMS/technologies.

Caractères interdits et sections CDATA

Une histoire de norme Unicode

Les flux XML doivent se contraindre à des standards établis par le w3c, parmis ces standards il existe une norme qui va nous intéresser tout particulièrement pour nos sites Drupal : celle concernant l'encodage des caractères de nos flux. Celle-ci précise que tous les caractères à l'intérieur de vos balises xml doivent se plier à la norme ISO/IEC 10646.

Il arrive que certain caractères saisis dans vos contenus, ne valident pas la norme ISO/IEC 10646. Ceci arrive notemment lorsque l'on créé des contenus à partir de copier/coller sauvages à partir d'un logiciel tier. Conséquence directe, notre flux ne passe pas la validation et une belle erreur est listée sur le validateur w3c :

Line 79, Column 68: non SGML character number 31

Nous allons voir comment chasser ces caractères indésirables.

Utilisation des sections CDATA pour assurer la compatibilité de vos flux

Une erreur classique dans la génération d'un flux xml est la présence de markup html dans les balises XML.
Pour éviter ce problème, Drupal converti en entitées HTML le contenu des champs lors de la génération de l'affichage de votre vue (et donc de votre flux). 

Cependant certaines visionneuses de flux ou logiciels d'importation peuvent avoir du mal à gérer les entitées HTML et se comportent mieux avec du code HTML "brut". Pour permettre l'inclusion de balises HTML dans notre XML, une solution toute simple est d'utiliser les sections CDATA :

  <item>
    <title>Titre de mon article</title>
    <link>http://blog.alantondelier.net/lien</link>
    <description>
      <![CDATA[ <h1>Ceci est un titre</h1> <img src="http://blog.alantondelier.net/uneimage.jpg" alt="Une image dans mon flux" /> <p>Blabla</p> ]]>
    </description>
  </item>

Couplées à la fonction html_entity_decode pour retrouver notre markup, les sections CDATA permettent d'assurer la compatibilité de nos flux XML tout en laissant le code valide.

Mise en place de la solution

Intéressons-nous à la mise en place d'une solution pour filtrer les mauvais caractères et pour ajouter les sections <[CDATA[]]>.

Nous allons intervenir au niveau du theming Drupal, et plus particulièrement au niveau du template de views générant le code pour chaque élément du flux.
Prenons l'exmple le plus courant : celui d'un flux RSS basique généré par views. Pour les autres cas la méthode présentée est à adapter selon vos besoins. 

Pour les flux RSS, le template appelé sera views-view-row-rss.tpl.php. Placez une copie de ce template dans votre theme et ouvrez le :

<?php

/**
 * @file
 * Default view template to display a item in an RSS feed.
 *
 * @ingroup views_templates
 */
?>
  <item>
    <title><?php print $title; ?></title>
    <link><?php print $link; ?></link>
    <description><?php print $description; ?></description>
    <?php print $item_elements; ?>
  </item>

Nous allons commencer par modifier le template pour décoder l'HTML et ajouter une section CDATA dans notre balise <description> (il est possible d'effectuer la même opération sur <title> si vous y affichez de l'html "brut").

    <description>
      <![CDATA[ <?php print html_entity_decode($description); ?> ]]>
    </description>

Il faut également retirer tous les caractères interdits par la norme XML. On utilise une fonction PHP de traitement de chaîne, empruntée ici, que l'on ajoute au template.php de notre theme :

/**
 * Removes invalid XML
 *
 * @access public
 * @param string $value
 * @return string
 */

function montheme_xmlcleaner($value)
{

    $ret = "";
    $current;
    if (empty($value))
    {
        return $ret;
    }

    $length = strlen($value);
    for ($i=0; $i < $length; $i++)
    {
        $current = ord($value{$i});
        if (($current == 0x9) ||
            ($current == 0xA) ||
            ($current == 0xD) ||
            (($current >= 0x20) && ($current <= 0xD7FF)) ||
            (($current >= 0xE000) && ($current <= 0xFFFD)) ||
            (($current >= 0x10000) && ($current <= 0x10FFFF)))
        {
            $ret .= chr($current);
        }
        else
        {
            $ret .= " ";
        }
    }
    return $ret;
}

On termine par ajouter l'appel à cette fonction dans notre template views-view-row-rss.tpl.php :

<?php

/**
 * @file
 * Default view template to display a item in an RSS feed.
 *
 * @ingroup views_templates
 */

?>
  <item>
    <title><?php print montheme_xmlcleaner($title); ?></title>
    <link><?php print $link; ?></link>
    <description>
      <![CDATA[ <?php print montheme_xmlcleaner(html_entity_decode($description)); ?> ]]>
    </description>
    <?php print $item_elements; ?>
  </item>

La magie opère : votre flux xml est valide. 

N'hésitez pas à partager vos expériences sur les flux xml & Drupal en commentaire ou si vous avez rencontré un problème différent sur le sujet.

Par AlanT
Alan Tondelier

Installer et personnaliser le module CKEditor pour Drupal 7

Installer et personnaliser le module CKEditor pour Drupal 7

La mise en place du module ckeditor pour Drupal peut s'avérer plus compliquée que prévue. Ce rapide guide liste les étapes à suivre pour installer sans encombres le module. Une seconde partie s'intéresse à l'installation de plugins complémentaire étendant les fonctionnalités de cet éditeur wysiwyg de qualité.

Ajouter ckeditor à votre Drupal

Récupération des fichiers

Tout commence par une petite commande drush bien sentie. Si vous ne connaissez pas drush, faites vrombir votre curseur au plus vite pour le récupérer, cet utilitaire en ligne de commandes est un indispensable du développement Drupal (j'y reviendrai). Attention les commandes ci-dessous ne fonctionnent qu'avec les dernières versions de drush, pour les plus anciennes vous devez d'abord télécharger le module (avec le switch "dl").

drush en ckeditor -y

Je vous conseille également de récupérer ckeditor_link qui vous permettra de créér des hyperliens vers vos contenus Drupal directement via l'interface de Ckeditor, indispensable !

drush en ckeditor_link -y

Une fois la récupération des modules faite, rendez vous sur http://ckeditor.com/download pour télécharger la librairie ckeditor.

Une petite personnalisation SEO-friendly

Afin de fournir du code plus moderne, apprécié des moteurs de recherches mais également pour fournir plus d'accessibilité à vos contenus, j'ai l'habitude d'effectuer une petite personnalisation de ckeditor avant de le télécharger : ajouter le support de la balise <figure> et <figcaption>.

Sur la page de ckeditor, prenez l'option "Or let met customize CKEditor" puis le bouton rouge.
Prendez le preset "Full" puis dans la partie basse, colonne de droite trouvez "Enhanced Image" et basculez-le colonne de gauche.

Si vous suivez cette étape, les  images que vous chargerez via CKEditor pourront être enrichies de la sémantique HTML5.


Le plugin Enhanced Image ajoutera les balises html5 figure et figcaption.

Ajoutez ensuite toutes les langues à ckeditor (j'ai rencontré des problèmes en ne choisissant que les langues utilisées sur mes sites), et récupérez votre librairie.

Une fois votre CKEditor téléchargé, placez le contenu de l'archive dans votre dossier /sites/all/modules/contrib/ckeditor/ckeditor .

CKFinder pour gérer vos fichiers

CKEditor ne sait gérer seul l'upload de fichiers sur votre serveur, pourtant il est très utile de pouvoir ajouter des visuels ou attacher des fichiers directement dans le corps de votre texte.
Heureusement, il existe des plugins pour remédier à ce manque. 

Le plus connu et utilisé d'entre eux se nomme CKFinder (payant mais possède une version de démonstration avec quelques limitations pour un usage non commercial).

Pour l'ajouter à votre CKEditor, les étapes sont les suivantes :

  1. Récupérez le plugin ici,
  2. Drupal cherchera par défaut ckfinder dans le répertoire /sites/all/modules/contrib/ckeditor/ckfinder. Dézippez-le à cet emplacement.
  3. Pour que le système de droits de Drupal gère correctement les permissions de ckeditor, vous devez modifier le fichier config.php de CKFinder.

Supprimez ces lignes (21-34) :

function CheckAuthentication()
{
    // WARNING : DO NOT simply return "true". By doing so, you are allowing
    // "anyone" to upload and list the files in your server. You must implement
    // some kind of session validation here. Even something very simple as...

    // return isset($_SESSION['IsAuthorized']) && $_SESSION['IsAuthorized'];

    // ... where $_SESSION['IsAuthorized'] is set to "true" as soon as the
    // user logs in your system. To be able to use session variables don't
    // forget to add session_start() at the top of this file.

    return false;
}

Et remplacez les par 

require_once '../../../../includes/filemanager.config.php';
  1. Finissez par éditer la variable $cookie_domain de votre settings.php
 $cookie_domain = '.votrenomdedomain.ext';

Votre CKFinder est maintenant fonctionnel.

Configuration de CKEditor

Les bases

Par défaut CKEditor a créé deux profils à partir des formats de textes installés par défaut (lors d'une mise en route standard de Drupal). Nous allons voir brievement la configuration d'un de ces profils.

La configuration des profils se fait dans admin/config/content/ckeditor/.

Modifiez l'un des profil qui vous intéresse et dans "Apparence de l'éditeur", choisissez les boutons de barre d'outil à faire apparaitre pour ce profil. Si vous souhaitez de plus activer le CKEditor Link, cochez la case dans la sous rubrique Plugins ET rendez vous dans la configuration du format de texte correspondant pour activer le filtre CKEditor Link afin d'avoir de belles URLs.

Je vous conseille également de limiter les balises dans "Nettoyer et afficher" > "Formats de polices de caractères". Bien souvent la balise H1 est inutile car déjà présente pour le titre de la page, d'autres balises peuvent selon vos cas être inutiles ou source de confusion pour les utilisateurs, à vous de faire le tri.

N'oubliez pas non plus d'activer CKFinder dans les "paramètres de l'explorateur de fichiers".

Une fois ces réglages effectués, CKEditor devrait être visible dans vos zones de textes possédant un format de texte associé à un profil.

Configurations avancées

Très vite, il devient intéressant de pouvoir définir ses propres styles dans ckeditor. Pour ce faire, dans les réglages d'un profil, dans CSS puis styles prédéfinis, définissez le chemin de ckeditor.styles.js, j'ai l'habitude d'utiliser cette syntaxte "%tjs/ckeditor.styles.js" pour appeler le fichier dans le theme principal du site (et non celui de l'administration comme le suggère Drupal !).

L'appel dans le thème évite de supprimer le fichier lors d'une mise à jour du module ou de la librairie.

Vous trouverez la copie originale de ckeditor.style.js dans le répertoire du module CKEditor. Voici un extrait du ckeditor.styles.js que j'utilise sur ce blog :

/*
Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

/*
 * This file is used/requested by the 'Styles' button.
 * The 'Styles' button is not enabled by default in DrupalFull and DrupalFiltered toolbars.
 */
if(typeof(CKEDITOR) !== 'undefined') {
    CKEDITOR.addStylesSet( 'drupal',
    [

            /* Block Styles */

            // code wrapper

            {

                name: 'Code Wrapper',
                element: 'pre',

            },

            /* Inline Styles */

            // These are core styles available as toolbar buttons. You may opt enabling
            // some of them in the "Styles" drop-down list, removing them from the toolbar.

            { name : 'Bleu'  , element : 'span', attributes : { 'class' : 'bleu' } },
            { name : 'Orange'  , element : 'span', attributes : { 'class' : 'orange' } },

            { name : 'Big'        , element : 'big' },
            { name : 'Small'      , element : 'small' },

            // code

            {
                    name : 'Code PHP',
                    element : 'code',
                    attributes :
                    {
                            'class' : 'language-php',
                    }
            },

            {
                    name : 'Code CSS',
                    element : 'code',
                    attributes :
                    {
                            'class' : 'language-css',
                    }
            },

            {
                    name : 'Code HTML',
                    element : 'code',
                    attributes :
                    {
                            'class' : 'language-markup',
                    }
            },

            {
                    name : 'Code JS',
                    element : 'code',
                    attributes :
                    {
                            'class' : 'language-javascript',
                    }
            },

...etc

Étendre CKEditor

Ajout de plugins

La communauté de CKEditor a créé un certain nombre de plugins téléchargeables sur cette page : http://ckeditor.com/addons/plugins/all.

Ces plugins s'incorporent directement au ckeditor de votre Drupal en téléchargeant l'archive du plugin, et en l'extrayant deux fois, dans le dossier /modules/contrib/ckeditor/plugins et dans /modules/contrib/ckeditor/ckeditor/plugins. (En réalité il n'est pas nécessaire de tout extraire en double, mais sachez que cette méthode est "sûre").

Une fois vos plugins ajoutés, leur activation se fait dans "Apparence de l'éditeur" > "Plugins".

Création de plugin

Il est évidemment possible de créer son propre plugin CKEditor au sein même de Drupal, je reviendrai sur ce point lors d'un prochain billet.

 

Par AlanT
Alan Tondelier

Lancement du Blog !

Lancement du Blog !

Enfin !
Après plusieurs semaines de questionnement personnel ponctuées principalement par des "faut-il ?", le Blog est finalement en ligne.

Qu'attendre de ce blog ?

Ce blog est né de ma volonté de partager des connaissances acquises au cours des 3 dernières années sur Drupal.
En effet, au fil des semaines passées a approfondir le CMS Drupal, j'ai été à plusieurs reprises surpris de ne pas lire plus d'articles sur des problèmes récurrents, rencontrés par de nombreuses personnes de la communauté et qui ne trouvent que des réponses partielles, incomplètes ou "alambiquées".

Sans avoir la prétention d'apporter des réponses "ultimes" ou parfaites, je veux avant tout partager avec ceux qui me liront (coucou !) des pistes cohérentes, fonctionnelles et permettant, je l'espère, d'apporter une brique de qualité à l'édifice Drupal.

Le blog d'un autodidacte sur la scène Drupal & Web

Qui suis-je ?
Pour faire court, je suis un ingénieur diplômé de Grenoble-INP - Pagora filière communication imprimée. Ayant toujours côtoyé de près les sites Internet, depuis l'époque où couinaient les modems 56K, j'ai en 2011 entrepris de tenter l'aventure web, d'abord à Paris puis à Rouen en Haute Normandie.
Après près de 3 années en agence, j'ai finalement décidé de prendre mon indépendance.

Je ponctuerai ce blog de quelques articles plus personnels afin de donner quelques retours d'expérience sur cette aventure numérique.

Une promesse avec moi-même

Il est très facile de ne pas tenir la rédaction d'un blog, aussi j'essaierai dans la mesure du possible de poster un article au minimum par semaine afin de faire vivre cet espace :)

Blog ... GO !

 

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

<?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' =>  array(
    // 'title' => t('Administer my module'),
    // 'description' => t('Perform administration tasks for my module.'),
    // ),
    'access multi-steps forms' => array(
      'title' => t('Access multi-steps forms'),
      'description' => t('Enable the user to fill-in custom multi-steps forms'),
    ),
  );
}

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

  $items['myform'] = array(
    'title' => t('My multi-steps form'),
    'page callback' => 'multi_etapes_myform_page',
    'access arguments' => array('access multi-steps forms'),
    'type' => MENU_NORMAL_ITEM,
    'file' => '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' => array('value' => t('Bio'), 'weight' => -103),
    '_myform_step2' => array('value' => t('Hobbies'), 'weight' => -102),
    '_myform_step3' => array('value' => t('Summary'), 'weight' => -101)
  ));
  // Ajaxify the form stepping process.
  $controls->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

<?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->name)?$user->name:'Guest';
  
  // Create parameters to be passed to the multi-step form
  $params = array('uname' => $uname);

  // Return Drupal renderable array.
  return array(
    'mform' => 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, &$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, &$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, &$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(&$form_state, &$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' => 'textfield',
    '#disabled'=>true,
    '#title' => t('Login'),
    '#default_value' => isset($vals['loginv']) ? $vals['loginv'] : $params['uname'], // get login name from previous page (not submited but for design purpose).
  );

  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#default_value' => isset($vals['name']) ? $vals['name'] : NULL,
  );
  $form['email'] = array(
    '#type' => 'textfield',
    '#title' => t('Email'),
    '#default_value' => isset($vals['email']) ? $vals['email'] : NULL,
    '#required' => TRUE,
  );
  $form['www'] = array(
    '#type' => 'textfield',
    '#title' => t('Your web site'),
    '#default_value' => isset($vals['www']) ? $vals['www'] : NULL,
  );

  // store the login in a hidden field so that the value is submited

  $form['loginv'] = array(
    '#type' => 'hidden',
    '#default_value' => 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, &$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(&$form_state, &$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' => 'textarea',
    '#title' => t('Movies'),
    '#default_value' => isset($vals['movies']) ? $vals['movies'] : NULL,
  );
  $form['books'] = array(
    '#type' => 'textarea',
    '#title' => t('Books'),
    '#default_value' => isset($vals['books']) ? $vals['books'] : NULL,
  );
  $form['sports'] = array(
    '#type' => 'textarea',
    '#title' => t('Sports'),
    '#default_value' => 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(&$form_state, &$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' => 'fieldset',
    '#title' => t('Summary'),
  );

  $form['summary']['sum_bio'] = array(
    '#markup'=>
    "<h3>".t('Bio')."</h3>
    <ul>
      <li><em>".t('Login')." :</em> ${vals1['loginv']}</li>
      <li><em>".t('Name')." :</em> ${vals1['name']}</li>
      <li><em>".t('e-mail')." :</em> ${vals1['email']}</li>
      <li><em>".t('Website')." :</em> ${vals1['www']}</li>
     </ul>",
  );

  $form['summary']['sum_hobbies'] = array(
    '#markup'=>
    "<h3>".t('Hobbies')."</h3>
    <ul>
      <li><em>".t('Movies')." :</em> ${vals2['movies']}</li>
      <li><em>".t('Books')." :</em> ${vals2['books']}</li>
      <li><em>".t('Sports')." :</em> ${vals2['sports']}</li>
     </ul>",
  );



  $data = mforms_get_vals('myform');

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

  $form['message_team'] = array(
    '#type' => 'textarea',
    '#title' => t('A message for the team'),
    '#default_value' => isset($vals['message_team']) ? $vals['message_team'] : NULL,
  );

  $form['confirm']=array(
    '#type' => 'checkbox',
    '#title' => 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, &$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, &$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'] && !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');

          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 ?

Par Mantalo Conseil
Benjamin Grapeloux
Agence web, Agence de Communication et Marketing en Dordogne (Aquitaine)

Nouveau site internet : Montgolfières du Périgord

Une solide base technologique

Fidèles à nos valeurs professionnelles, nous avons conseillé à Boris et à Martine Nigrowsky de porter une attention particulière sur les fondamentaux du web :

  • la qualité et l'évolutivité du socle technique (nous avons préconisé Drupal et Drupal Commerce),
  • la richesse du contenu (structure, sémantique, engagement personnel),
  • l'ergonomie des pages (facilité d'utilisation pour les visiteurs),
  • la fluidité visuelle (esthétique et équilibre des éléments graphiques),
  • les fonctionnalités d'administration (accessibles aux propriétaires du site).

Nous nous sommes rapidement compris et avons ainsi pu commencer à travailler sur d'excellentes bases.

Une billetterie électronique simple et efficace

L'enjeu était de permettre à une clientèle essentiellement touristique de pouvoir acheter en ligne ses billets sur l'écran d'un téléphone. Il fallait donc :

  • un site Responsive permettant à tous les mobinautes d'accéder facilement à la billetterie,
  • un process d'achat simple et rapide, bien que très complet car réserver un vol en montgolfière entraîne des contraintes particulières (techniques et règlementaires),
  • une administration fonctionnelle permettant une gestion simple de la billetterie.

Bref, un maximum d'informations en en minimum de clics !

Page d'accueil des Montgolfières du Périgord

Une ligne éditoriale dans l'esprit de l'entreprise

D'une façon très classique, Martine, Boris et leurs collaborateurs ont bien d'autres choses à faire que de rédiger les pages de leur site internet, même s'ils ne manquent pas de choses intéressantes à dire !

Leur valeur ajoutée étant clairement de satisfaire leurs clients en leur offrant de superbes tranches de vie dans leurs nacelles, ils nous ont confié la rédaction des pages et des actualités de leur site internet.

Une ligne graphique dans l'esprit de leur passion

Fort d'un logo flambant neuf, le site des montgolfières du Périgord se devait d'être à la fois agréable et accessible (contrastes conformes aux référentiels d'accessibilité).

Nous vous invitons à visiter le nouveau site internet des montgolfières du Périgord, et d'en juger par vous même !

Vous vous dites qu'il serait peut être temps pour vous aussi de refondre le site Internet de votre entreprise? Contactons-nous et échangeons sans tarder pour passer à l'action !

Par Mantalo Conseil
Benjamin Grapeloux
Agence web, Agence de Communication et Marketing en Dordogne (Aquitaine)

Nouveau site internet : Montgolfières du Périgord

Une solide base technologique

Fidèles à nos valeurs professionnelles, nous avons conseillé à Boris et à Martine Nigrowsky de porter une attention particulière sur les fondamentaux du web :

  • la qualité et l'évolutivité du socle technique (nous avons préconisé Drupal et Drupal Commerce),
  • la richesse du contenu (structure, sémantique, engagement personnel),
  • l'ergonomie des pages (facilité d'utilisation pour les visiteurs),
  • la fluidité visuelle (esthétique et équilibre des éléments graphiques),
  • les fonctionnalités d'administration (accessibles aux propriétaires du site).

Nous nous sommes rapidement compris et avons ainsi pu commencer à travailler sur d'excellentes bases.

Une billetterie électronique simple et efficace

L'enjeu était de permettre à une clientèle essentiellement touristique de pouvoir acheter en ligne ses billets sur l'écran d'un téléphone. Il fallait donc :

  • un site Responsive permettant à tous les mobinautes d'accéder facilement à la billetterie,
  • un process d'achat simple et rapide, bien que très complet car réserver un vol en montgolfière entraîne des contraintes particulières (techniques et règlementaires),
  • une administration fonctionnelle permettant une gestion simple de la billetterie.

Bref, un maximum d'informations en en minimum de clics !

Page d'accueil des Montgolfières du Périgord

Une ligne éditoriale dans l'esprit de l'entreprise

D'une façon très classique, Martine, Boris et leurs collaborateurs ont bien d'autres choses à faire que de rédiger les pages de leur site internet, même s'ils ne manquent pas de choses intéressantes à dire !

Leur valeur ajoutée étant clairement de satisfaire leurs clients en leur offrant de superbes tranches de vie dans leurs nacelles, ils nous ont confié la rédaction des pages et des actualités de leur site internet.

Une ligne graphique dans l'esprit de leur passion

Fort d'un logo flambant neuf, le site des montgolfières du Périgord se devait d'être à la fois agréable et accessible (contrastes conformes aux référentiels d'accessibilité).

Nous vous invitons à visiter le nouveau site internet des montgolfières du Périgord, et d'en juger par vous même !

Vous vous dites qu'il serait peut être temps pour vous aussi de refondre le site Internet de votre entreprise? Contactons-nous et échangeons sans tarder pour passer à l'action !

Par GoZ
Fabien CLEMENT

Soumettre un formulaire généré avec la formapi en GET sans les valeurs form_id et form_build_id

Il serait dommage de générer un formulaire "à la main" sous Drupal alors qu'il y a la [formapi](https://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/7) pour nous faciliter la vie.

Le problème à résoudre ici est d'utiliser la formapi pour générer une formulaire d'envoi en GET mais sans être pollué par les variables de drupal en URL (form_id, form_build_id, op etc).

en lire plus

Par admin

Assemblée générale extraordinaire du 18 mars 2014

Fichier attaché Taille
dff-ag-2014.pdf 82.41 Ko

Chers adhérents,

J’ai le plaisir de vous convier à une assemblée général extraordinaire de notre association le mardi 18 mars prochain, conformément à l’article 8 des statuts. Cette assemblée extraordinaire sera immédiatement suivie d’une assemblée générale ordinaire.

Cette réunion se tiendra le mardi 18 mars 2014 à partir de 19h à la Maison des Association du 3ème, 5 rue Perrée, 75003 Paris, dans la salle 1.

L’assemblée générale extraordinaire aura pour objet la soumission au vote de modifications de nos statuts, notamment sur la constitution du conseil d’administration et sur son mode de scrutin. L’objectif de cette AGE est également de recaler dans le temps les assemblées générale afin de respecter les statuts (dans les 3 mois suivant la fin de l’exercice). Nous vous transmettrons dans les prochains jours la nouvelle version proposée des statuts.

L’assemblée générale ordinaire qui suivra aura à l’ordre du jour :
Rapport moral présenté par le président
Rapport financier présenté par le trésorier
Élection du nouveau conseil d’administration
Questions ouvertes
Les membres désirant présenter leur candidature au conseil d’administration et ceux qui souhaitent inclure des points complémentaires à l’ordre du jour devront se manifester au plus tôt au bureau via email à bureau@listes.drupalfr.org

Si vous ne pouvez être physiquement présent lors du vote, vous pouvez vous faire représenter par un autre membre de l’association muni d‘un pouvoir régulier (ou vous pouvez l’envoyer à l’adresse de l’association au minimum 4 jours avant l’assemblée générale). Le pouvoir est disponible dans le document joint.

Vous pouvez également participer à l'assemblée générale par un moyen de communication électronique permettant de vous identifier formellement. Dans ce cas, si vous souhaitez participer aux votes vous devrez renoncer à l'anonymat des votes afin de les transmettre.

Je vous rappelle également que seuls les membres à jour de leur cotisation peuvent participer à l’assemblée générale et participer au vote, n'hésitez pas à adhérer dés à présent grâce au bulletin d'adhésion en ligne http://drupalfr.org/sites/default/files/formulaire-adherent-drupalfr.pdf ou sur place le jour même.

Cordialement
Olivier Friesse
Président Drupal France et Francophonie

En page d'accueil : 
Par benftwc

Drupal : utiliser, c’est bien, contribuer, c’est mieux !

Sous Drupal, nous le savons, il existe une myriade de modules, tous plus intéressant que les autres. Seulement, savez-vous qui se cache derrière ? Toute une communauté !

Un module, avant d’être utilisé par des milliers de sites, doit passer par quelques étapes, à commencer par la sandbox. Cette Sandbox permet aux développeurs d’envoyer leur modules à la communauté afin d’obtenir des retours, des tests, et des patchs.

Je vais vous parler un peu plus en détail des patchs.

Un « patch », kézako ?

Un patch est tout « simplement » un fichier texte, contenant un différentiel produit par GIT. Pour avoir une idée, vous pouvez exécuter cette ligne :

$ git diff

Vous devriez voir apparaître toutes les modifications apportées sur tout les fichiers de la branche courante sous cette forme :

diff --git a/currency_converter.admin.inc b/currency_converter.admin.inc
index e644435..487862f 100644
--- a/currency_converter.admin.inc
+++ b/currency_converter.admin.inc
@@ -11,7 +11,7 @@
  */
 function currency_converter_admin_settings() {

-  $default_weight = array('BYR' => 0, 'RUB' => 1, 'EUR' => 2, 'USD' => 3, 'CAD' => 4, 'PLN' => 5, 'UAH' => 6, 'CNY' => 7, 'LTL' => 8, 'LVL' => 9);
+  $default_weight = array('BYR' => 0, 'RUB' => 1, 'EUR' => 2, 'USD' => 3, 'CAD' => 4, 'PLN' => 5, 'UAH' => 6, 'CNY' => 7, 'LTL' => 8, 'LVL' => 9, 'JPY' => 10, 'GBP' => 11, 'CHF' => 12, 'AUD' => 13, 'HKD' => 14, 'INR' => 15);
   $weight = variable_get('currency_converter_weight', $default_weight);
   asort($weight);

Comme vous pouvez le remarquer, certaines lignes débutent par un « - », d’autres par un « + ». Simplement, les lignes avec « - » ont étés supprimées, celles avec un « + » ajoutées.

Comment créer un patch

Pour créer un patch, c’est très simple, peu de connaissances GIT sont requises.

Une fois vos modifications effectuées, testées puis validées, vous n’avez qu’une commande à entrer pour générer le patch :

 $ git diff > fichier.patch

Et c’est tout ! Il vous restera ensuite de suivre les règles de nommage, puis d’envoyer votre patch sur Drupal.

Contribuer au module, de A à Z

Bien, voici donc les étapes détaillées à suivre afin de maintenir un module


## Première étape : On récupère notre projet, sous la bonne version
$ git clone --branch X.x-X.x http://git.drupal.org/project/projectname.git projectname
$ cd projectname

## Nous sommes donc dans le répertoire crée par GIT, contenant les sources du module

A présent que le projet est copié, nous pouvons travailler dedans et effectuer nos modifications

## A présent que toutes nos modifications sont faites, nous pouvons passer à la création du patch
$ git diff > [description de ce que vous avez modifié][numéro du ticket][numéro du commentaire].patch

A présent, vous savez créer vos patch et les envoyer. Regardons comment en utiliser un.

Appliquer un patch

Une autre façon de contribuer est d’appliquer les patchs pour les tester et faire un retour au développeur.

Encore une fois, très peu de connaissances GIT sont requises

$ git apply -v fichier.patch

Vous êtes fins prêt.

Partez à la conquete de nouveaux modules à débugger ! Drupal.org vous propose également ces 2 liens :

Patch Bingo et Bug Bingo

Vous y trouverez de manière aléatoire un module / thème qui nécessite une intervention.

Vous trouverez d’autres infos sur les slides de @simongeorges et @Artusamak, disponibles ici

The post Drupal : utiliser, c’est bien, contribuer, c’est mieux ! appeared first on Benftwc.

Pages