Développer son propre plugin Drupal Crumbs
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.
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.
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 :
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.
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.
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 ?
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 :
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.
Nous allons faire un formulaire d'exemple en 3 étapes :
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.'));
}
}
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 datamulti_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 functionif(!_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.'));
}
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.
Quelques astuces complémentaires.
Lors de la réalisation du formulaire multi-étape pour le site applicatif de l'eGrid, j'ai eu besoin d'executer du code javascript pour réaliser un carrousel (étape Évaluation).
Le problème avec l'execution de javascript dans les appels en ajax Drupal, c'est qu'il est toujours délicat de savoir à quel moment se "brancher" pour executer son code. Le morceau de javascript suivant permet d'executer le code souhaité à l'étape désirée. Attention cependant le code est réinterprété a chaque reconstruction du DOM (et donc à chaque affichage de message d'erreur de validation).
J'ai placé le code dans js/multi_etapes.js et modifié multi_etapes.info pour déclarer le fichier js ( scripts[] = js/multi_etapes.js ).
(function ($, Drupal, window, document, undefined) {
Drupal.behaviors.multi_etapes = {
attach: function(context, settings) {
if(context === document || context[0].nodeName == 'FORM'){
var step = $('[id^="edit-steps-btns-wrapper"] .current-step').attr('id');
// si on est bien sur un formulaire multi-étapes
if(step){
var regex = /step(\d)/;
var match = regex.exec(step);
step = parseInt(match[1]);
alert('Etape ' + step);
// on peut envisager un switch pour gérer chaque étape.
}
}
}
}
})(jQuery, Drupal, this, this.document);
Si vous vous aventurez dans la réalisation d'un tel formulaire avec javascript, je serai très interessé par vos retours et vos solutions pour bien gérer l'execution JS.
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 ?
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.
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.
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.
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.
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é.
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.
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.
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 .
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 :
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';
$cookie_domain = '.votrenomdedomain.ext';
Votre CKFinder est maintenant fonctionnel.
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.
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
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".
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.
Enfin !
Après plusieurs semaines de questionnement personnel ponctuées principalement par des "faut-il ?", le Blog est finalement en ligne.
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.
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.
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 !
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.
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 :
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é.
Pour la suite de cet article, j'utiliserai la bibliothèque dompdf pour générer mes fichiers.
Installer la librairie dompdf
Pour installer simplement la librairie dompdf, il vous faut :
Dompdf est installé, vous pouvez peaufiner vos réglages dans <admin>/config/user-interface/print .
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...) :
[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) :
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 !
Drush est un outil en ligne de commande permettant d'administrer simplement Drupal. Disponible sur toutes les plateformes (unix/osx/windows), il est un indispensable du développement de vos sites.
La commande make de Drush permet de préparer rapidement une installation de Drupal avec ses modules contribués, ses librairies pré-téléchargées et plus encore. Incontournable quand on travaille sur un grand nombre de projets où l'on utilise à 80% les mêmes modules.
La commande drush make permet de récupérer les sources de projets Drupal (core, module, themes...) à partir des dépots officiels. Afin de décrire quels éléments récupérer, la commande make doit être accompagnée d'un fichier .make. Description et exemple d'un fichier .make :
mydrupal.make
; Base Drupal
core = 7.x
; Internal API for Drush make
api = 2
; Drupal Version
projects[drupal][version] = "7.26"
; Modules
; If no version precised, fetch the last stable one
; see projets[views] for an example
projects[admin_menu][subdir] = "contrib"
projects[adminimal_admin_menu][subdir] = "contrib"
projects[backup_migrate][subdir] = "contrib"
projects[ctools][subdir] = "contrib"
projects[ckeditor][subdir] = "contrib"
projects[ckeditor_link][subdir] = "contrib"
projects[context][subdir] = "contrib"
projects[devel][subdir] = "contrib"
projects[google_analytics][subdir] = "contrib"
projects[jquery_update][subdir] = "contrib"
projects[libraries][subdir] = "contrib"
projects[metatag][subdir] = "contrib"
projects[pathauto][subdir] = "contrib"
projects[pathologic][subdir] = "contrib"
projects[token][subdir] = "contrib"
projects[transliteration][subdir] = "contrib"
projects[variable][subdir] = "contrib"
; Declare the use of version 3.6 of views
projects[views][subdir] = "contrib"
projects[views][version] = "3.6"
projects[webform][subdir] = "contrib"
; Themes
projects[adminimal_theme][type] = "theme"
projects[mothership][type] = "theme"
; Libraries
; Please fill the following out. Type may be one of get, git, bzr or svn,
; and url is the url of the download.
libraries[lessphp][download][type] = "file"
libraries[lessphp][download][url] = "http://leafo.net/lessphp/src/lessphp-0.4.0.tar.gz"
libraries[lessphp][directory_name] = "lessphp"
libraries[lessphp][type] = "library"
libraries[ckeditor][download][type] = "file"
libraries[ckeditor][download][url] = "http://ckeditor.com/online-builder/releases/minified/4.3.3/moono/4.3.3/ckeditor_4.3.3.zip"
libraries[ckeditor][directory_name] = "ckeditor"
libraries[ckeditor][destination] = "modules/contrib/ckeditor"
Comme vous pouvez le voir la structure est très simple. Le fichier commence par déclarer la version de Drupal à utiliser, liste les projets eque vous souhaitez automatiquement télécharger ainsi que les thèmes. Les fichiers .make peuvent également récupérer directement les librairies dont vous avez besoin.
Une fois ce fichier créé et enregistré, il vous suffit d'invoquer Drush avec cette syntaxe :
drush make mydrupal.make mywebsite -y
Drush créera votre installation de Drupal dans le dossier mydrupal, le switch -y répondra par l'affirmative à tous les prompts de la console.
Le téléchargement des fichiers terminé, vous vous retrouvez avec votre dossier Drupal prêt à l'utilisation, vous n'avez plus qu'à executer install.php.
Pour une liste exhaustive des fonctionnalités de drush make, vous pouvez lire la documentation officielle (en anglais) ici
Il est légitime de se poser la question : "Pourquoi ne pas passer par un profil d'installation Drupal ?"
Les fichiers make possèdent un certain avantage face aux profils d'installation :
Le principal point noir de Drush make est sa relative lenteur pour récupérer les fichiers des serveurs Drupal, là où un profil d'installation possède déjà tous les fichiers à installer.
Pour générer vos fichiers .make, vous pouvez simplement ouvrir votre IDE favori et taper à la main le fichier. À préférer lorsque vous savez exactement ce que vous voulez.
Une autre façon de faire est d'utiliser le site http://drushmake.me qui vous assistera dans la création de vos fichiers .make. Pratique !
Vous avez un site Drupal "référence" et vous souhaiteriez en sortir un fichier .make ? C'est possible !
Avec drush, placez vous dans le répertoire Drupal du site considéré puis :
drush generate-makefile /chemin/du/fichier-make.make
Et votre fichier .make sera automatiquement généré. Selon votre projet, des informations manquantes peuvent survenir, notamment si vous avez développé des modules custom. Éditez votre fichier .make en conséquence de façon à avoir une base propre de déploiement.
Remarque : En utilisant generate-makefile, la version de chaque module sera précisé dans le fichier .make, de façon à avoir un Drupal à jour je vous conseille de supprimer les lignes précisant la version de vos modules.
Ainsi, avec Drush make, vous accélérerez considérablement votre mise en place de projets Drupal, en local ou sur vos serveurs.
Le module field_collections permet de créer des "champs de champs", particulièrement pratique pour accélérer la création de contenus et permettre de simplifier la gestion en back-office. Si vous avez déjà travaillé avec field-collection, vous avez pu vous heurter à un problème lors de la modification de la sortie HTML, ou bien essayé de "themer" différement un des items de votre field-collection.
Cet article propose un template regroupant l'ensemble de la sortie html d'un field-collection, permettant ainsi d'en simplifier la personnalisation.
Note : Cet article est une réponse "simpliste" à un problème de la communauté Drupal datant du 14 Mai 2011 : https://drupal.org/node/1157794, actuellement la solution proposée n'a pas encore été implémentée. Elle le sera dans la version 2.x de field-collection qui rendra cet article obsolète.
Lorsque vous mettez en place un field-collection, drupal va rendre l'html de sortie en passant par ces étapes :
Donc, si je veux personnaliser entièrement la sortie html de mes field-collection , parce-que j'ai envie de faire une intégration html aux petits oignons, la mission va s'avérer assez délicate.
Avant tout, la solution proposée n'a pas vocation de remplacer une vraie modification du coeur du module field-collection, bien au contraire, ce que je propose est à mi chemin entre un "hack" et une utilisation conventionnelle des fonctions de theming de Drupal.
Aussi vous pouvez regarder du côté du topic officiel sur drupal.org pour un patch du module permettant (normalement) d'arriver au même résultat.
Cette solution a cependant l'avantage de faire le travail demandé, à savoir personnaliser au maximum la sortie de vos field-collection mais également d'être applicable sans aucune modification de module ou d'installations complémentaires ; en travaillant sur le template.php de votre thème et en modifiant deux fichiers .tpl.php.
Et dès que la version 2.x de field_collection sera sortie, le problème ne se posera plus.
Nous allons travailler dans template.php, remplacez "votre_theme" par le nom système de votre thème "front" :
template.php - hook_preprocess_node()
/**
* Override or insert variables into the node template.
*/
function votre_theme_preprocess_node(&$variables) {
if ($variables['view_mode'] == 'full' && node_is_page($variables['node'])) {
$variables['classes_array'][] = 'node-full';
}
// Field Collection Alter - This is required in order to pass the description of the field collection and the "add new item" to $variables.
if (!empty($variables['content'])) {
foreach ($variables['content'] as $key => $field) {
if (isset($field['#field_type']) && $field['#field_type'] === 'field_collection' && $field['#formatter'] === 'field_collection_view') {
// move description and links from #suffix into separate variables for field_collection_view;
if ($field['#formatter'] === 'field_collection_view') {
// remove #prefix - if not, get printed
$variables['content'][$key]['#prefix'] = '';
//capture links and description - this is hackish (but works) !
preg_match("/<div .*>(.*)<\/div>./i", $field['#suffix'], $desc);
preg_match("/(<ul.*<\/ul>)/i", $field['#suffix'], $links);
// field_collection global description
if (isset($desc[1])) {
$variables['content'][$key]['fc_description'] = $desc[1];
}
// field_collection links (add new item to the collection)
if (isset($links[1])) {
$variables['content'][$key]['fc_links'] = $links[1];
}
//remove #suffix - if not, get printed
$variables['content'][$key]['#suffix'] = '';
}
}
}
}
}
template.php - hook_preprocess_field
/**
* Override or insert variables into the field template.
*/
function votre_theme_preprocess_field(&$vars) {
if (isset($vars['element']['#field_type']) && $vars['element']['#field_type'] === 'field_collection' && $vars['element']['#formatter'] === 'field_collection_view') {
// move each item into a renderable array and pass html for edit and delete links into simple variables
foreach ($vars['items'] as $key => &$item) {
// item
$item['item'] = array_shift($item['entity']);
// edit
if (isset($item['links']['#links']['edit'])) {
$edit = $item['links']['#links']['edit'];
$edit = l($edit['title'], $edit['href'], array('query' => $edit['query'],'attributes'=>array('class'=>'field-collection-edit-item')));
$item['item']['#edit_link'] = $edit;
}
// delete
if (isset($item['links']['#links']['delete'])) {
$delete = $item['links']['#links']['delete'];
$delete = l($delete['title'], $delete['href'], array('query' => $delete['query'],'attributes'=>array('class'=>'field-collection-delete-item')));
$item['item']['#delete_link'] = $delete;
}
// pass a nice, renderable array to template file
$item = $item['item'];
}
// add field-collection description
if (isset($vars['element']['fc_description'])) {
$vars['fc_description'] = $vars['element']['fc_description'];
}
// add field-collection links
if (isset($vars['element']['fc_links'])) {
$vars['fc_links'] = $vars['element']['fc_links'];
}
}
}
Ainsi, nous avons modifié les données transmises au fichiers templates. Les fichiers templates utilisés pour rendre les field-collection n'ont pas changé, il s'agit toujours de field.tpl.php pour l'enveloppe et field-collection-item.tpl.php pour chaque item de la collection. Or avec ce qui a été fait dans template.php, field-collection-item.tpl.php doit renvoyer la plus simple des structure (ie ne pas ajouter d'html).
field-collection-item.tpl.php - à placer dans votre thème
<?php
print render($content);
?>
Maintenant la partie qui nous intéresse le plus, le template de rendu de votre field-collection : field.tpl.php. Si vous avez suivi le code, vous avez du voir que nous avons rendu disponible le contenu normalement affiché dans field-collection-item directement au niveau de field.tpl.php.
Votre template field.tpl.php est utilisé pour rendre l'html de tout les champs de votre site, vous cibler uniquement les field-collection, nommez le field--field-collection.tpl.php, vous pouvez également mettre différents templates par field-collection et par affichage selon la syntaxe :
field--field-collection.tpl.php - à placer dans votre thème
<code class="language-markup"><!-- field-collection wrapper -->
<div class="field-collection">
<?php if (!empty($fc_description)): // description of the field-collection ?>
<div class="field-collection-description">
<?php print $fc_description; ?>
</div>
<?php endif; ?>
<?php foreach ($items as $delta => $item): // loop on each item of the collection ?>
<div class="field-collection-item">
<?php
print render($item);
// or play with some fields
// print render($fc_item['my_field']);
?>
<?php if (isset($item['#edit_link']) || isset($item['#delete_link'])): // check that links are available (can be enabled/disabled in display settings) ?>
<div class="field-collection-item-links">
<?php print isset($item['#edit_link'])?$item['#edit_link']:NULL; ?>
<?php print isset($item['#delete_link'])?$item['#delete_link']:NULL; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php print !empty($fc_links)?$fc_links:NULL; // link for adding a new item to the collection ?>
</div>
<!-- end field-collection wrapper -->
Videz vos caches, et normalement votre nouveau template prend le relai dans l'affichage de vos field-collections. À noter que ce template est utilisé uniquement lors de l'affichage "field collection items".
J'espère que cet article vous sera utile, et n'hésitez pas à faire des retours si vous l'implémentez dans vos projets Drupal :)
Par design, les blocks Drupal doivent être placés dans des régions, en-tête, contenu, footer, sidebar, ce mécanisme permet facilement de structurer et de réorganiser rapidement les sections de nos sites.
Cependant il arrive d'avoir besoin d'afficher rapidement un block à l'intérieur même du texte d'un article, ceci peut survenir par exemple lorsque l'on souhaite écrire un premier paragraphe, insérer un block issu d'une vue Drupal puis continuer la rédaction. Je vous présente une méthode simple pour arriver à ce résultat.
Afin de placer nos blocks dans le contenu texte des nodes, il faut trouver une solution simple et sécurisée. Le bourrin pourra appeler directement les blocks par du code PHP, mais ce comportement pourra induire de bonnes failles de sécurité.
La solution proposée ici est d'utiliser une chaîne avec une syntaxe prédéfinie qui sera traitée en aval :
{--!block!{module-qui-genere-le-block}{nom-systeme-du-block} # <em><strong>Ici un commentaire</strong> </em>! #--}
Ainsi, en plaçant cette chaîne à l'intérieur de vos textes on évite tout code php et la présence d'un commentaire permet d'indiquer à vos éditeurs ce que fait cette chaîne de caractère "étrange" (et leur évite ainsi d'y toucher...).
Il va falloir maintenant récupérer cette chaîne de caractère et travailler avec hook_preprocess_node afin de récupérer le block qui nous intéresse. À partir d'ici deux solutions :
Évidemment, la seconde méthode est la meilleure : réutilisabilité du code, possibilité de construire une page d'administration du module (par exemple pour définir quels fields peuvent contenir des blocks et pas seulement le field body), gestion des droits, meilleure intégration dans l'éditeur wysiwyg, etc...
Cependant, mon but ici est avant tout de vous donner une piste qui fonctionne, aussi je vous laisse libre de décider ou traiter cette chaîne.
Dans les deux cas, tout se passe avec hook_preprocess_node :
hook_preprocess_node - ou hook est le nom de votre thème OU de votre module
For instance we could also fetch a full view, with filters and sorting, with this method...function hook_preprocess_node(&$variables, $hook) {
if(isset($variables['content']['body'])){
$regexGlobal = '/{--(?P<capture>.*)--}/';
$regexMod = '/!(?P<module>[a-z]+)!/';
$regexParams = '/{(?P<params>(\w|-)+)}/';
// Get the body value, this can be extended to other fields
$body = $variables['content']['body'][0]['#markup'];
if (preg_match($regexGlobal, $body, $matches)) { // if special strings are found
$capture = $matches['capture'];
preg_match($regexMod, $capture, $module);
switch ($module = $module['module']) {
case 'block':
// Block code, i used a switch here for extention purpose.preg_match_all($regexParams, $capture, $params); // get block params
$params = $params['params'];
// fetch block
$block = block_load($params[0], $params[1]);
// the following call ensure that rights, contextual links, and so are functionnal
$block = _block_get_renderable_array(_block_render_blocks(array($block)));
$block = drupal_render($block);
// replace special string with block HTML
$variables['content']['body'][0]['#markup'] = preg_replace($regexGlobal, $block, $body);
break;
case 'my_other_module':
// do stuff
break;
default:
break;
}
}
}
}
Ainsi vous avez une méthode simple et facile à mettre en place pour afficher vos blocks directement dans vos contenus. Bien entendu ceci est un point de départ, à vous d'étendre ce code pour vos besoins.
Ce mécanisme est utilisé sur la page suivante : meetsys.com - les clients.
Vue de l'administration :
Récemment, je vous ai donné un rapide aperçu des nouveautés et challenges que Drupal 8 allait introduire. Je vous expliquais entre autres que bon nombre de modules allaient vraisemblablement disparaître. Cela va des modules stables sous Drupal 6 qui n'ont jamais eu de version stable pour Drupal 7 et n'en auront donc apparemment jamais sous Drupal 8, mais aussi des modules qui ne franchiront tout simplement pas le pas entre D7 et D8, pour quelque raison que ce soit.
La question peut se poser car si l'on en croit la roadmap de développement, nous devrions pouvoir avoir une version stable à la fin de l'été 2013, pour autant que les bugs critiques aient été réglés. Autant dire que lorsque vous voudrez commencer un site dessus ou répondre à une demande client, il vaudra mieux être prêt vu l'éténdue des changements (c'est un euphémisme).
Voilà l'année 2013 qui commence et DrupalFacile qui fête quasiment ses deux ans et demi. Comme chaque année, c'est le moment de vous dévoiler quelques statistiques sur le site et vous allez voir que les choses progressent plutôt bien, preuve que la demande d'informations et d'aide sur Drupal croît constamment :
Sur chaque page de projet Drupal, plusieurs informations extrêmement importantes et/ou pratiques existent. L'une d'entre elles vous aura peut-être échappée et concerne le dépôt git du projet que vous pouvez manipuler en ligne de plusieurs façons. Vous y avez accès très facilement, en suivant le lien Repository viewer depuis le bloc Development dans la barre latérale de droite.
Si vous suivez de près l'actualité Drupal, cela ne vous aura pas échappé. NodeSquirrel vient récemment d'ouvrir ses portes après une longue beta pour se placer comme LE spécialiste de la sauvegarde hors-site de vos sites Drupal. Il faut dire que l'équipe qui propose ce service est la même que celle derrière le module Backup and Migrate, ce qui lui donne une crédibilité certaine quand on connaît la popularité du module et à quel point il fonctionne bien.
En janvier dernier, lors d'un vote sur les sujets que les membres de la communauté lyonnaise voulaient voir traiter dans les prochains meetups, le sujet de l'optimisation des performances Drupal a comme très souvent refait son apparition. En revanche, l'angle demandé était un peu moins habituel car assez spécifique. Comment optimiser les performances Drupal, donc, mais "par le cache" uniquement. Sujet assez dense et technique, mais qui est vraiment intéressant tant il est vaste et complexe.
Les 21, 22 et 23 juin 2013 se déroulera DrupalCamp Paris, un des moments phares de la communauté Drupal française. Pour que cet événement soit pleinement réussi et suscite des vocations et une adoption encore plus grande, on a besoin de nombreuses sessions de qualité.
Voilà DrupalCamp Paris 2013 terminé. 350 personnes se sont réunies dans le centre de conférence Microsoft à Issy Les Moulineaux pour parler Drupal pendant 2 jours et assister à des sessions toutes plus intéressantes les unes que les autres. Le dimanche c'était le sprint habituel dans les locaux de Smile à Levallois-Perret.
L'année 2013 aura littéralement été une année en demi-teinte pour DrupalFacile. Les 6 premiers mois de l'année ont été intenses avec des vidéos postées régulièrement et même un record de traffic battu en mars (6924 visiteurs uniques). Hélas, depuis avant l'été, le site est au point mort et DrupalFacile reçoit régulièrement des encouragements et des demandes de vidéos qui sont précieusement consignées !
Pour le dernier meetup Drupal Lyon avant la coupure de l'été, j'ai fait une présentation sur la sécurité. Puisque le panel est assez hétérogène et qu'on parle aussi bien à des nouveaux venus sur Drupal qu'à des experts, je me suis dit qu'il serait intéressant de faire une présentation à la fois high-level et qui récapitule un ensemble de bonnes pratiques sur la sécurité qu'on a parfois tendance à oublier. L'idée principale c'est : ne pensez pas être "nul" en sécurité ou pas assez expert pour en parler ou vous y coller.
Le module Drupal Crumbs permet de maitriser son fil d’Ariane (Breadcrumb) selon des critères très complets (views, token, taxonomy, entity reference, etc). Son système de plugins lui permet de supporter toute entité créée sur votre site Drupal. Découvrons comment implémenter notre propre plugin Crumbs pour supporter une entité sur mesure ou encore fournie par un module contribué.