Planète

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(&amp;$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) &gt; 1){

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

    foreach ($crumbs as $key =&gt; $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>

    &lt;script type="text/php"&gt;
      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-&gt;get_width();
        $h = $pdf-&gt;get_height();

        $footer = $pdf-&gt;open_object();

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

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

        $pdf-&gt;close_object();
        $pdf-&gt;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" =&gt; "{PAGE_NUM}", "!total" =&gt; "{PAGE_COUNT}"));
        $pdf-&gt;page_text($w - 15 - $width, $y, $pagenumtxt, $font, $size, $color);
      }
    &lt;/script&gt;

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 AlanT
Alan Tondelier

Simplifiez et accélérez vos installations Drupal avec Drush make

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.

Une installation Drupal prête à l'emploi

C'est quoi Drush make ?

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.

Drush make console
Sortie de drush make sur un environnement windows

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

Drush make VS profil d'installation

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 :

  1. Vous récupérez automatiquement la dernière version stable du module,
    Tout en gardant la possibilité de préciser la version d'un module en particulier en rajoutant la ligne "projects[nomprojet][version] = "7.2", où 7.2 est la version de votre module.
  2. Il est plus facile de maintenir un fichier make qu'un ensemble de dossiers de modules, de plus vous pouvez facilement mettre en place des fichiers make pour chaque typologie de projets : site_corporate.make, site_multilingue.make, site_vitrine.make ...
  3. L'utilisation de drush make permet d'automatiser facilement le déploiment de Drupal dans des scripts bash (unix) ou batch (windows). J'y reviendrai.

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.

Générer facilement ses fichiers .make

La méthode manuelle & semi-manuelle

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 !

Génération du .make à partir d'un site Drupal existant

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.

 

Par AlanT
Alan Tondelier

Personnalisez jusqu&#039;à la moelle le template de vos field-collections

Troll matryoshka

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.

Field Collection, anatomie d'un theming compliqué


Sortie par défaut de field-collection avec formatteur "Field collection items"

Lorsque vous mettez en place un field-collection, drupal va rendre l'html de sortie en passant par ces étapes :

  1. Ajout de l'html pour les variables (modifier/supprimer/ajouter) directement dans une fonction de theming interne au module field_collection,
  2. Passage au fichier de template field.tpl.php pour mettre en place l'html d'enveloppe du champ,
  3. Passage au fichier de template field-collection-item.tpl pour rendre l'html de chaque item de field-collection.

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.

Une solution "maison"

Ce n'est pas la plus élégante des solutions !

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.

En attendant...

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(&amp;$variables) {
  if ($variables['view_mode'] == 'full' &amp;&amp; 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 =&gt; $field) {

      if (isset($field['#field_type']) &amp;&amp; $field['#field_type'] === 'field_collection' &amp;&amp; $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("/&lt;div .*&gt;(.*)&lt;\/div&gt;./i", $field['#suffix'], $desc);
            preg_match("/(&lt;ul.*&lt;\/ul&gt;)/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(&amp;$vars) {

  if (isset($vars['element']['#field_type']) &amp;&amp; $vars['element']['#field_type'] === 'field_collection' &amp;&amp; $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 =&gt; &amp;$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' =&gt; $edit['query'],'attributes'=&gt;array('class'=&gt;'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' =&gt; $delete['query'],'attributes'=&gt;array('class'=&gt;'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

&lt;?php
  print render($content);
?&gt;

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 :

  1. field--[nom_systeme_du_field].tpl.php
  2. field--[nom_systeme_du_field]--[nom_systeme_affichage].tpl.php

field--field-collection.tpl.php - à placer dans votre thème

<code class="language-markup">&lt;!-- field-collection wrapper --&gt;
&lt;div class="field-collection"&gt;

&lt;?php if (!empty($fc_description)): // description of the field-collection ?&gt;

  &lt;div class="field-collection-description"&gt;

    &lt;?php print $fc_description; ?&gt;

  &lt;/div&gt;

&lt;?php endif; ?&gt;

&lt;?php foreach ($items as $delta =&gt; $item): // loop on each item of the collection ?&gt;

  &lt;div class="field-collection-item"&gt;
    &lt;?php
      print render($item);
      // or play with some fields
      // print render($fc_item['my_field']);
    ?&gt;

    &lt;?php if (isset($item['#edit_link']) || isset($item['#delete_link'])): // check that links are available (can be enabled/disabled in display settings) ?&gt;

      &lt;div class="field-collection-item-links"&gt;
        &lt;?php print isset($item['#edit_link'])?$item['#edit_link']:NULL; ?&gt;
        &lt;?php print isset($item['#delete_link'])?$item['#delete_link']:NULL; ?&gt;
      &lt;/div&gt;

    &lt;?php endif; ?&gt;

  &lt;/div&gt;

&lt;?php endforeach; ?&gt;

  &lt;?php print !empty($fc_links)?$fc_links:NULL; // link for adding a new item to the collection ?&gt;

&lt;/div&gt;
&lt;!-- end field-collection wrapper --&gt;

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".


Veillez à mettre le format de sortie sur "field collection items", sinon le template par défaut sera utilisé.

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 AlanT
Alan Tondelier

Afficher n&#039;importe quel block dans le corps de vos articles 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.

Une syntaxe pour les appeler

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...).

Un hook pour les générer

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 :

  1. On traite notre chaîne dans le template.php du thème de rendu front,
  2. On met en place un module qui va traiter cette chaîne.

É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

function hook_preprocess_node(&amp;$variables, $hook) {

  if(isset($variables['content']['body'])){

  $regexGlobal = '/{--(?P&lt;capture&gt;.*)--}/';
  $regexMod = '/!(?P&lt;module&gt;[a-z]+)!/';
  $regexParams = '/{(?P&lt;params&gt;(\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.
For instance we could also fetch a full view, with filters and sorting, with this method...
          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.

Et dans un exemple les lier

Ce mécanisme est utilisé sur la page suivante : meetsys.com - les clients.

Vue de l'administration :


Affichage d'un block dans le corps du texte avec cette méthode

 

Par anavarre

Les modules sur lesquels parier dans le futur

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.

Par anavarre

Faut-il apprendre Drupal 8 maintenant ?

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).

Par anavarre

Bilan 2012 et perspectives 2013

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 :

Par anavarre

Savez-vous vous servir de drupalcode.org ?

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.

Par anavarre

Sauvegardez vos sites Drupal avec NodeSquirrel

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.

Par anavarre

Présentation : Optimiser les performances Drupal par le cache

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.

Par anavarre

Bilan 2013 et perspectives 2014

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 !

Par anavarre

Meetup Drupal Lyon - Présentation sur la sécurité

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.

Développer son propre plugin Drupal Crumbs







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

Thème 
Fil d'ariane
Modules
Drupal 7

Par juliendubreuil
julien dubreuil
Drupal since 2009

Drupal Commerce, ajouter un pane custom dans votre checkout

Drupal Commerce creation d'un pane de checkout

Comme nous avons pu le voir dans l’article précédent, il est simple de configurer le checkout de Drupal Commerce de façon à créer l’expérience utilisateur que vous désirez.

Dans cet article nous allons voir comment avec du code, ajouter et modifier des panes. Pour rappel, les panes sont les éléments qui composent les différentes pages du checkout. On y retrouve par exemple, le pane de paiement, d’adresse ou encore de choix de solution de transport.

Histoire d’illustrer cela, je vous propose de créer un module Drupal afin d’ajouter un pane permettant à vos clients de choisir parmi plusieurs emballages cadeaux. Ce simple module est accessible sur Github pour plus de compréhension.

Déclaration de notre nouveau Pane

La première chose à faire consiste à déclarer au système notre nouveau pane en utilisant le

hook_commerce_checkout_pane_info()

sandbox.module

1
2
3
4
5
6
7
8
9
<span class='line'><span class="k">function</span> <span class="nf">sandbox_commerce_checkout_pane_info</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>  <span class="nv">$panes</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>    <span class="s1">&#39;title&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Paper gifts&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;base&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;paper_gift_pane&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;page&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;checkout&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="nv">$panes</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span>

Notre pane sera alors reconnu par Drupal et disponible dans le checkout. Dès à présent, vous pouvez activer le module et voir le pane apparaitre sur la page d’administration du checkout.

Comme à la manière des hooks de Drupal, le code qui génère l’affichage des panes va faire de l’introspection dans le code, de façon à chercher toutes les fonctions qui commenceront par la “base” que vous avez définie dans le hook_commerce_checkout_pane_info().

Ainsi le code sera à la recherche des fonctions suivantes :

  • BASE_settings_form()
  • BASE_checkout_form()
  • BASE_checkout_form_validate()
  • BASE_checkout_form_submit()
  • BASE_review()

Implémentation du formulaire d’administration du Pane

L’intérêt du pane est d’être affiché sur une page du checkout, néanmoins vous pouvez créer un formulaire de configuration pour celui-ci. Pour y accéder, rendez-vous sur l’interface d’administration du checkout. Notez qu’il n’y a pas de table dans laquelle les informations seront stockées, il vous faudra utiliser les méthodes variable_get() et variable_set() pour sauvegarder vos réglages.

sandbox.module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: settings form callback.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_settings_form</span><span class="p">(</span><span class="nv">$checkout_pane</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="nv">$form</span><span class="p">[</span><span class="s1">&#39;paper_gifts&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>    <span class="s1">&#39;#type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;checkboxes&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;#title&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Ship this item in a gift box&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;#default_value&#39;</span> <span class="o">=&gt;</span> <span class="nx">variable_get</span><span class="p">(</span><span class="s1">&#39;paper_gifts&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">()),</span>
</span><span class='line'>    <span class="s1">&#39;#options&#39;</span> <span class="o">=&gt;</span> <span class="nx">paper_gift_pane_default_paper_gifts</span><span class="p">(),</span>
</span><span class='line'>  <span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="nv">$form</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Defines a list of default paper gifts to use.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_default_paper_gifts</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">return</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>    <span class="s1">&#39;boy&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Boys&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;girl&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Girls&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;christmas&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Christmas&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;birthday&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Birthday&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;none&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;None&#39;</span><span class="p">),</span>
</span><span class='line'>  <span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span>

Dans le code ci-dessus, j’ai créé un formulaire d’administration et ajouté un champ texte, permettant au webmaster de modifier le texte qui sera affiché à l’utilisateur.

Création du Pane

Un pane n’est ni plus, ni moins qu’un formulaire dans le formulaire de checkout. Comme à l’habitude, vous y retrouverez le form, le form_validate et le form_submit.

Form

Le form contiendra ce qui sera affiché à l’utilisateur. Dans le cas de notre exemple, l’utilisateur pourra choisir entre plusieurs papiers cadeaux ou pas d’emballage.

sandbox.module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: form callback.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_checkout_form</span><span class="p">(</span><span class="nv">$form</span><span class="p">,</span> <span class="o">&amp;</span><span class="nv">$form_state</span><span class="p">,</span> <span class="nv">$checkout_pane</span><span class="p">,</span> <span class="nv">$order</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="nv">$paper_gifts</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
</span><span class='line'>  <span class="nv">$default_paper_gifts</span> <span class="o">=</span> <span class="nx">paper_gift_pane_default_paper_gifts</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">foreach</span> <span class="p">(</span><span class="nv">$default_paper_gifts</span> <span class="k">as</span> <span class="nv">$name</span> <span class="o">=&gt;</span> <span class="nv">$title</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="nv">$image</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>      <span class="s1">&#39;path&#39;</span> <span class="o">=&gt;</span> <span class="nx">drupal_get_path</span><span class="p">(</span><span class="s1">&#39;module&#39;</span><span class="p">,</span> <span class="s1">&#39;sandbox&#39;</span><span class="p">)</span> <span class="o">.</span> <span class="s1">&#39;/images/&#39;</span> <span class="o">.</span> <span class="nv">$name</span> <span class="o">.</span> <span class="s1">&#39;.jpg&#39;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;title&#39;</span> <span class="o">=&gt;</span> <span class="nv">$title</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;alt&#39;</span> <span class="o">=&gt;</span> <span class="nv">$title</span><span class="p">,</span>
</span><span class='line'>    <span class="p">);</span>
</span><span class='line'>    <span class="nv">$paper_gifts</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="nx">theme</span><span class="p">(</span><span class="s1">&#39;image&#39;</span><span class="p">,</span> <span class="nv">$image</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="nv">$pane_form</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>    <span class="s1">&#39;#type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;radios&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;#default_value&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;none&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;#title&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Select your paper gift&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;#description&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Your items will be shipped in a gift box.&#39;</span><span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;#options&#39;</span> <span class="o">=&gt;</span> <span class="nv">$paper_gifts</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;#attributes&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;class&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;paper-gift-pane-selection&#39;</span><span class="p">)),</span>
</span><span class='line'>  <span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="nv">$pane_form</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">][</span><span class="s1">&#39;#attached&#39;</span><span class="p">][</span><span class="s1">&#39;css&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>    <span class="nx">drupal_get_path</span><span class="p">(</span><span class="s1">&#39;module&#39;</span><span class="p">,</span> <span class="s1">&#39;sandbox&#39;</span><span class="p">)</span> <span class="o">.</span> <span class="s1">&#39;/css/sandbox.css&#39;</span><span class="p">,</span>
</span><span class='line'>  <span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="nv">$pane_form</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span>

Form validate

Le form_validate permet d’effectuer des opérations de validation sur les champs saisis par l’utilisateur.

sandbox.module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: form validation callback.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_checkout_form_validate</span><span class="p">(</span><span class="nv">$form</span><span class="p">,</span> <span class="o">&amp;</span><span class="nv">$form_state</span><span class="p">,</span> <span class="nv">$checkout_pane</span><span class="p">,</span> <span class="nv">$order</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// Validate the given value or set to none if null.</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$form_state</span><span class="p">[</span><span class="s1">&#39;values&#39;</span><span class="p">][</span><span class="nv">$checkout_pane</span><span class="p">[</span><span class="s1">&#39;pane_id&#39;</span><span class="p">]]))</span> <span class="p">{</span>
</span><span class='line'>    <span class="nv">$pane_values</span> <span class="o">=</span> <span class="nv">$form_state</span><span class="p">[</span><span class="s1">&#39;values&#39;</span><span class="p">][</span><span class="nv">$checkout_pane</span><span class="p">[</span><span class="s1">&#39;pane_id&#39;</span><span class="p">]];</span>
</span><span class='line'>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="k">empty</span><span class="p">(</span><span class="nv">$pane_values</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]))</span> <span class="p">{</span>
</span><span class='line'>      <span class="nv">$pane_values</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;none&#39;</span><span class="p">;</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>  <span class="k">return</span> <span class="k">TRUE</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span>

Ici, on regarde simplement s’il y a une valeur, sinon on définit que l’utilisateur ne veut pas d’emballage.

Form submit

La sauvegarde des informations intervient dans le form_submit une fois que l’on a passé la validation.

sandbox.module

1
2
3
4
5
6
7
8
9
10
11
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: form submission callback.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_checkout_form_submit</span><span class="p">(</span><span class="nv">$form</span><span class="p">,</span> <span class="o">&amp;</span><span class="nv">$form_state</span><span class="p">,</span> <span class="nv">$checkout_pane</span><span class="p">,</span> <span class="nv">$order</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$form_state</span><span class="p">[</span><span class="s1">&#39;values&#39;</span><span class="p">][</span><span class="nv">$checkout_pane</span><span class="p">[</span><span class="s1">&#39;pane_id&#39;</span><span class="p">]]))</span> <span class="p">{</span>
</span><span class='line'>    <span class="nv">$pane_values</span> <span class="o">=</span> <span class="nv">$form_state</span><span class="p">[</span><span class="s1">&#39;values&#39;</span><span class="p">][</span><span class="nv">$checkout_pane</span><span class="p">[</span><span class="s1">&#39;pane_id&#39;</span><span class="p">]];</span>
</span><span class='line'>    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$pane_values</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]))</span> <span class="p">{</span>
</span><span class='line'>      <span class="nv">$order</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$pane_values</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">];</span>
</span><span class='line'>    <span class="p">}</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span>

Remarquez que vous avez accès à l’objet $order et que vous pouvez faire ce que vous en voulez. Pour l’exercice j’aurais pu créer un nouveau line item, mais pour faire simple le choix de l’utilisateur résidera comme simple information dans la commande.

Form review

Une fois l’information attachée à la commande, il ne nous reste plus qu’à la faire paraitre sur le pane de review afin que l’utilisateur puisse la voir.

Pour cela il suffit d’implémenter le form_review

sandbox.module

1
2
3
4
5
6
7
8
9
10
11
12
<span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Pane: Review</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">paper_gift_pane_review</span><span class="p">(</span><span class="nv">$form</span><span class="p">,</span> <span class="nv">$form_state</span><span class="p">,</span> <span class="nv">$checkout_pane</span><span class="p">,</span> <span class="nv">$order</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="nv">$order</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="nv">$order</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]</span>  <span class="o">!=</span> <span class="s1">&#39;none&#39;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="nv">$content</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>      <span class="s1">&#39;#type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;item&#39;</span><span class="p">,</span>
</span><span class='line'>      <span class="s1">&#39;#markup&#39;</span> <span class="o">=&gt;</span> <span class="nx">t</span><span class="p">(</span><span class="s1">&#39;Items in your order will be wrapped with the %paper paper gift&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;%paper&#39;</span> <span class="o">=&gt;</span> <span class="nv">$order</span><span class="o">-&gt;</span><span class="na">data</span><span class="p">[</span><span class="s1">&#39;paper_gift&#39;</span><span class="p">])),</span>
</span><span class='line'>    <span class="p">);</span>
</span><span class='line'>    <span class="k">return</span> <span class="nx">drupal_render</span><span class="p">(</span><span class="nv">$content</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span>

Modification d’un Pane existant

Comme mentionné précédemment, les panes ne sont que des forms et comme tout form vous avez la possibilité de les alterer via un hook_form_alter() ou encore mieux un hook_form_ID_alter().

Dans le cas ou vous voudriez modifier les paramètres d’un pane, vous implémentez la fonction hook_commerce_checkout_pane_info_alter()

Le mot de la fin

Au final, ce n’était pas si compliqué que cela ? En plus du fait d’avoir maintenant un pane pour offrir un emballage papier cadeau à vos clients, vous savez maintenant qu’il y a 3 grandes étapes dans la vie d’un pane. Le formulaire de settings pour l’admin, le formulaire destiné aux clients et le formulaire de review. Je serais curieux de savoir pourquoi vous avez besoin d’ajouter un pane dans votre checkout…
Comme mentionné au début de ce post, le code est accéssible sur Github.

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

e-commerce : un catalogue vendeur sans budget photo

Image appareil photo et caddie

Un site marchand sans photos attrayantes... juste inimaginable !

Imaginez une boutique sans vitrine ou un catalogue commercial sans illustrations... un remède contre la vente !

Plus encore, l'image a pris une telle importance dans nos comportements d'achat qu'elle ne doit pas seulement exister, elle se doit aussi d'être attractive pour satisfaire notre sensibilité croissante au marketing sensoriel.

Les studios spécialisés dans la photo publicitaire offrent sans aucun doute la meilleure solution sur le plan technique. Sans avoir participé un jour à un shooting produits de A à Z, on imagine difficilement le temps et les ressources nécessaires... ce qui nous laisse pantois lorsqu'on reçoit les devis.

Le coût d'un shooting produits en studio

Toujours trop cher lorsque le budget global est serré, les devis des photographes sont toutefois généralement justifiés. Comptez globalement entre 900 et 1000 euros HT pour une vingtaine de photos avec le retraitement numérique et les droits d'exploitation.

Évidemment, si on considère q'un produit doit en moyenne faire l'objet de 3 vues différentes, et qu'une boutique en ligne doit offrir une gamme de produits suffisante pour être attractive (au moins 50 références hors déclinaisons), le budget atteint assez facilement 7500 euros, soit environ autant que le budget minimum permettant le développement d'un site marchand générique de qualité satisfaisante.

Alternatives aux photos professionnelles

Rares sont donc les jeunes e-commerçants qui ont recours aux services d'un studio professionnel. Pour autant, disposer de belles photos produits reste essentiel et pour ne pas condamner le site marchand à l'échec dès le départ, il faut s'organiser au mieux avec les ressources disponibles.

Installez un studio de forture dans vos locaux

N'importe quelle pièce peut faire l'affaire pour peu que vous puissiez mobiliser environ 10 m2durant quelques semaines, avec :

  • un point de lumière naturelle,
  • un plateau de présentation pour les prises de vue,
  • une étagère (ou des suspensions) pour y entreposer les accessoires ou les produits en cours de travail,
  • une zone de circulation pour accéder facilement aux tables sans déplacer le pied de l'appareil photo et les dispositifs d'éclairage.

Réaliser toutes les prises de vues à partir de la même installation garantit l'homogénéité des photos et du résultat final.

Disposez du bon matériel

L'outillage indispensable se résume à 3 équipements :

  • Un trépied pour stabiliser l'appareil photo au cours des réglages et des prises de vues,
  • Un appareil photo numérique qui permet un réglage manuel de l'ouverture et de l'exposition,
  • Une boite à lumière ou des sources de lumière continue d'une puissance suffisante et adaptée aux produits à shooter.

Pour le reste, faites appel à votre imagination et à votre ingéniosité :

  • usez de papier ou de toile pour le fond,
  • mettez à profit le ruban adhésif et le fil de nylon pour suspendre et fixer le fond sur son support,
  • tracez au crayon ou au blanco vos repères de positionnement sur la table de présentation,
  • montez une étagère en kit pour tout ranger et tout avoir sous la main,
  • ...

Considérez l'éclairage comme essentiel

Rassurez-vous, les plus grands professionnels passent eux aussi beaucoup de temps à trouver la bonne organisation des sources lumineuses.

Le bon choix d'éclairage permet des prises de vues qui respectent les couleurs et leurs contrastes. N'hésitez donc pas à passer le temps nécessaire aux essais car une fois trouvée, la solution servira durant toute la durée du shooting photo. Pensez aussi à garder sous la main un quart de feuille blanche qui vous servira à étalonner vos photos sur la plan de la luminosité.

Les ombres peuvent être parasites, ou de précieux alliés pour souligner les formes et donner du réalisme à vos produits.

Soignez la présentation des produits

Il reste la mise en scène des produits et sur ce point, vous pouvez simplement vous inspirer de certains sites référents qui impulsent les tendances pour des produits similaires aux votres. Bien sûr, si votre créativité vous autorise à innover... surprenez-nous !

5 clés pour valoriser à coup sûr les prises de vues de vos produits :

  • Inspectez vos produits sous toutes les coutures pour ne laisser aucun détail dégrader leur aspect (micro tâches, peluches, traces, grains de poussière, ...),
  • Trouvez l'angle le plus favorable pour mettre en évidence leurs signes distinctifs (rotation, inclinaison, surplomb, ...),
  • Associez vos produits à des objets très courants (clés, montre, carte de crédit, ...) si vous souhaitez que vos clients aient une bonne appréciation de leurs dimensions sans avoir à les rechercher dans le descriptif,
  • Enrichissez la présentation de vos produits par la présence d'accessoires s'ils contribuent fortement à intensifier l'intérêt ou le plaisir sensoriel,
  • Assurez-vous avant chaque prise de vue que votre fond soit resté impécable (exempt de plis, tâches, accrocs, ...) afin d'éviter les retouches post-production toujours ruineuses en temps.

Toutes les astuces sont permises pour imprimer un mouvement, maintenir en suspend un élément, raidir un support, ... et là, c'est à vous de jouer.

Focus sur la prise de vues

De la même façon que les repères sur votre fond guideront vos dispositions sur le plateau de shooting, le ou les points de prise de vues seront exactement tracés au sol, une fois définies comme satisfaisantes.

Prévoyez de demander à votre prestataire web une fonctionnalité zoom sur vos photos. Vos clients pourront ainsi examiner vos produits dans leurs moindres détails, et ils adorent ça... pour peu que les angles de prise de vues anticipent cette observation.

Doublez enfin toujours vos photos d'une prise de vue avec un quart de feuille blanche contre votre produit, face à l'angle de shoot. Vous aurez ainsi un repère constant de luminosité. Il sera un précieux indice pour guider le retraitement numérique définitif.

Traiter les photos avant intégration dans la boutique

Le retraitement numérique de vos photos se justifie par :

  • la volonté de montrer à vos clients, des photos totalement fidèles à l'aspect réel de vos produits,
  • l'intérêt de suivre les préconisation d'un guide de style que vous aurez préalablement établi en précisant notamment :
    • les contraintes de taille des images,
    • la dimension des différentes marges,
    • les couleurs de background,
    • les alignements pour les différentes catégories de produits,
    • les ombrages et leurs caractéristiques,
  • le respect des principales règles visuelles pour la vente en ligne telles qu'exigées par certaines plateformes de vente en ligne comme eBay, Amazon ou Google Shopping, si vous étiez amenés à les utiliser.

Cette opération est la moins accessible pour les e-commerçants qui ne maîtrisent pas les outils de retouche numérique. C'est sur ce point qu'ils peuvent mettre à profit les services de notre agence.

À propos de la Web agency Mantalo

Lorsque le budget de nos clients le permet, nous collaborons volontiers avec les studios spécialisés en photographie publicitaire.

Dans les autres cas, à l'instar de cette publication ouverte et compte tenu de notre souhait de contribuer à la réussite de leur commerce en ligne, nous les aidons à mettre en oeuvre les moyens requis pour produire des photos de produits dans les meilleures conditions par un accompagnement :

  • à la mise en place d'un studio "maison" dans leurs locaux,
  • à la formalisation d'un guide de style,
  • à la prise de vues.

Nous offrons également à nos clients, des prestations de retraitement numérique à l'aide de logiciels professionnels qui nécessitent une licence d'utilisation et une formation spécialisée.

Category: 

Votre projet de e-commerce tient la route ?

Votre budget photo est trop réduit pour faire appel à un studio professionnel en photographies publicitaires ?

Contactez-nous et voyons ensemble comment optimiser vos ressources disponibles !

Par vincent59
Vincent Liefooghe

Influence du paramétrage APC sur les performances Drupal

Avec une version PHP inférieure à 5.5 il est recommandé d'utiliser un cache d'OpCode. Ce cache permet d'améliorer les performances de PHP, en mettant en cache le code PHP une fois qu'il a été analysé.

L'un des caches les plus utilisés est APC. Son installation sur une distribution type Debian consiste simplement en une commande

apt-get install php-apc

Par défaut, la taille mémoire réservée est de 32 Mo.

Si cette taille n'est pas optimale, les performances peuvent être dégradées au lieu d'être améliorées. En effet, on a alors un phénomène de saturation du cache, de fragmentation, et le code doit passer par des phases de check / miss / insert.

Pour nos tests, nous réalisons une première séance de tirs avec l'outil siege, pendant 2 minutes, sur une liste de 200 urls (les contenus ont été générés par le module Devel generate).

Résultats sans APC

Transactions:		         770 hits
Availability:		      100.00 %
Elapsed time:		      119.15 secs
Data transferred:	        3.97 MB
Response time:		        3.81 secs
Transaction rate:	        6.46 trans/sec
Throughput:		        0.03 MB/sec
Concurrency:		       24.63
Successful transactions:         766
Failed transactions:	           0
Longest transaction:	        4.65
Shortest transaction:	        0.55

Typiquement, sans Cache d'OpCode et sans cache Drupal, les performances sont assez faibles.

Résultats avec 16 Mo

Le résultat de la commande siege est :

Transactions:		        2928 hits
Availability:		      100.00 %
Elapsed time:		      119.51 secs
Data transferred:	       14.98 MB
Response time:		        1.02 secs
Transaction rate:	       24.50 trans/sec
Throughput:		        0.13 MB/sec
Concurrency:		       24.89
Successful transactions:        2917
Failed transactions:	           0
Longest transaction:	        2.78
Shortest transaction:	        0.55

Sur cette VM de test, on obtient 24 transactions par secondes, sans cache drupal activé. On constate que la taille mémoire utilisée par le cache est de 15 Mo environ. Par rapport à la configuration sans le cache APC, on augmente les performances d'un rapport 4 environ.

La consommation mémoire APC est la suivante :

Résultats avec 12 Mo

Transactions:		         784 hits
Availability:		       99.87 %
Elapsed time:		      119.73 secs
Data transferred:	        3.99 MB
Response time:		        3.75 secs
Transaction rate:	        6.55 trans/sec
Throughput:		        0.03 MB/sec
Concurrency:		       24.58
Successful transactions:         778
Failed transactions:	           1
Longest transaction:	        4.42
Shortest transaction:	        0.58

Dans ce cas, le paramétrage du cache APC n'est pas correct. En effet, la taille du cache n'est pas suffisante pour stocker toutes les opérations.
Du coup, les performances chutent drastiquement (environ 1/4 des performances précédentes), et l'on a des performances identiques au fonctionnement sans le cache APC.

On constate que le cache APC est saturé, et que l'on voit des "cache full count", qui signifient que le cache a été rempli et qu'il a fallu vider des éléments, ce qui est très pénalisant.

Résultats avec 24 Mo

Transactions:		        3104 hits
Availability:		      100.00 %
Elapsed time:		      119.67 secs
Data transferred:	       15.90 MB
Response time:		        0.96 secs
Transaction rate:	       25.94 trans/sec
Throughput:		        0.13 MB/sec
Concurrency:		       24.89
Successful transactions:        3085
Failed transactions:	           0
Longest transaction:	        1.53
Shortest transaction:	        0.48

Avec 24 Mo, les choses rentrent dans l'ordre. Le cache est correctement utilisé, mais il n'y a pas d'amélioration des performances.

Résultats avec 32 Mo

Transactions:		        2595 hits
Availability:		      100.00 %
Elapsed time:		      119.78 secs
Data transferred:	       13.48 MB
Response time:		        1.15 secs
Transaction rate:	       21.66 trans/sec
Throughput:		        0.11 MB/sec
Concurrency:		       24.87
Successful transactions:        2583
Failed transactions:	           0
Longest transaction:	        5.92
Shortest transaction:	        0.89

Conclusion

Cette série de tests a montré que le paramétrage du cache APC doit être régulièrement monitoré et validé, sous peine d'avoir un effet inverse à celui attendu et de dégrader les performances.

On peut également constater qu'il ne sert à rien de surallouer la mémoire, car une fois la taille optimale allouée, il n'y a aucun gain.

Cela prouve également que le choix de l'hébergeur est important, et que la richesse du CMS Drupal a également des impacts sur les performances de la plate-forme. Il convient donc de faire le bon choix. Un hébergement mutualisé ne pourra généralement pas offrir la souplesse d'un hébergeur spécialisé, capable de mettre en oeuvre les bons composants et les bons paramétrages.

Addendrum : impact du cache Drupal

Les tests ont été réalisés avec le cache désactivé. De ce fait, de nombreuses requêtes MySQL sont générées, et tendent à ralentir le débit.
Si on active le cache Drupal pour les utilisateurs anonymes, les résultats sont spectaculaires.

Un premier essai sans cache APC, mais avec le cache Drupal :

Transactions:		        3610 hits
Availability:		      100.00 %
Elapsed time:		      119.95 secs
Data transferred:	       18.70 MB
Response time:		        0.83 secs
Transaction rate:	       30.10 trans/sec
Throughput:		        0.16 MB/sec
Concurrency:		       24.91
Successful transactions:        3598
Failed transactions:	           0
Longest transaction:	        5.18
Shortest transaction:	        0.59

On constate une nette amélioration par rapport à un paramétrage sans cache (30 transactions / seconde vs 6.5). Au final, on est assez proche d'une configuration APC sans cache Drupal.

Deuxième essai avec le cache Drupal et le cache APC :

Transactions:		       23611 hits
Availability:		      100.00 %
Elapsed time:		      119.03 secs
Data transferred:	      122.00 MB
Response time:		        0.13 secs
Transaction rate:	      198.36 trans/sec
Throughput:		        1.02 MB/sec
Concurrency:		       24.93
Successful transactions:       23483
Failed transactions:	           0
Longest transaction:	        2.49
Shortest transaction:	        0.06

Lorsqu'on active les caches APC et Drupal, les performances sont multipliées par 30 !

N'oubliez donc pas, pour avoir de bonnes performances, d'activer tous les niveaux de cache possibles : au niveau Drupal, mais également au niveau PHP. Le choix d'un hébergeur qui supporte ces différents niveaux est un gage de performances. Mon hébergeur, HebInWeb.com, met en oeuvre APC sur les instances Drupal.

 

Catégorie: 


Tag: 

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

Nouveau site vitrine pour La Chanteracoise

La Chanteracoise passe la vitesse supérieure

Pour Sylvain Boucher aussi, les sites Internet formatés par les sociétés commerciales du Web ont démontré leur limite. Enfermée dans un contrat de service qui ne lui laissait d'autres choix que celui d'attendre son terme, La Chanteracoise attendait patiemment le moment de pouvoir se libérer... c'est chose faite :

  • Le nouveau site est en ligne,
  • Il permet une gestion autonome du contenu,
  • L'investissement consacré au développement du site étant réalisé, le seul coût annuel sera désormais celui de l'hébergement !

Dans la continuité de notre accompagnement marketing

L'ensemble des missions parallèlement consacrées à la stratégie marketing pour permettre à La Chanteracoise de développer son activité, a été mis à profit dans la structure du nouveau site.

La segmentation des gammes et des différents circuits de distribution qui référencent la marque a pu contribuer à la structure très cohérente du contenu. Les univers graphiques des gammes cohabitent également en parfaite harmonie pour porter la pertinence des packaging et des visuels produits.

Une formation à la gestion du contenu

L'équipe commerciale est désormais formée à toutes les interventions récurrentes qui permettront à La Chanteracoise d'enrichir et d'actualiser les pages de son site Internet. À partir d'une arborescence préalablement posée et des vocabulaires de taxonomie précisément définis par l'agence pour optimiser la navigation sur le site, le service marketing de La Chanteracoise a pu intégrer très simplement ses textes de présentation et d'information.

Pour renforcer le positionnement de la marque

Site web de la Biscotterie La Chanteracoise, adaptable pour tout support mobile et fixeComme très souvent lors de la création ou de la refonte du site Internet d'une entreprise commerciale, l'objectif est multiple :

  • toucher sur la toile les publics que cherche à prospecter la marque,
  • impacter le marché par l'attractivité et l'amplitude des gammes de produits,
  • lier une relation professionnelle avec les réseaux de distribution et centrale de référencement,
  • communiquer ses valeurs et son engagement auprès des clients finaux,
  • contribuer au développement des ventes et des parts de marché.

Convaincu par les résultats très significatifs constatés à la suite des premières actions préconisées par notre agence, Sylvain nous a naturellement confié son projet Web en connaissance de cause.

Drupal pour voir plus loin

À celles et ceux qui imaginent que la puissance de Drupal implique un back office très complexe, la facilité et la rapidité avec lesquelles l'équipe de La Chanteracoise s'est appropriée les outils de publication démontrent qu'il n'en est rien ! Il appartient à l'agence de se mettre un minimum à la place de son client pour :

  • configurer un espace d'administration qui soit à la fois fonctionnel et agréable,
  • faire en sorte que la gestion du contenu soit un plaisir,
  • adapter les accès, les rôles et les droits des utilisateurs aux besoins avérés d'interventions récurrentes sur le contenu.

Côté public, la richesse et la diversité de Drupal et de ses nombreux modules ouvrent la voie toute grande à une ergonomie très favorable pour l'interactivité entre les contenus, l'accessiblité et la navigation mobile.

Bon, assez parlé de communication ! Envie de faire croustiller vos petits déjeuners et vos goûters gourmands ? Direction la-chanteracoise.fr !

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

Nouveau site vitrine pour La Chanteracoise

La Chanteracoise passe la vitesse supérieure

Pour Sylvain Boucher aussi, les sites Internet formatés par les sociétés commerciales du Web ont démontré leur limite. Enfermée dans un contrat de service qui ne lui laissait d'autres choix que celui d'attendre son terme, La Chanteracoise attendait patiemment le moment de pouvoir se libérer... c'est chose faite :

  • Le nouveau site est en ligne,
  • Il permet une gestion autonome du contenu,
  • L'investissement consacré au développement du site étant réalisé, le seul coût annuel sera désormais celui de l'hébergement !

Dans la continuité de notre accompagnement marketing

L'ensemble des missions parallèlement consacrées à la stratégie marketing pour permettre à La Chanteracoise de développer son activité, a été mis à profit dans la structure du nouveau site.

La segmentation des gammes et des différents circuits de distribution qui référencent la marque a pu contribuer à la structure très cohérente du contenu. Les univers graphiques des gammes cohabitent également en parfaite harmonie pour porter la pertinence des packaging et des visuels produits.

Une formation à la gestion du contenu

L'équipe commerciale est désormais formée à toutes les interventions récurrentes qui permettront à La Chanteracoise d'enrichir et d'actualiser les pages de son site Internet. À partir d'une arborescence préalablement posée et des vocabulaires de taxonomie précisément définis par l'agence pour optimiser la navigation sur le site, le service marketing de La Chanteracoise a pu intégrer très simplement ses textes de présentation et d'information.

Pour renforcer le positionnement de la marque

Site web de la Biscotterie La Chanteracoise, adaptable pour tout support mobile et fixeComme très souvent lors de la création ou de la refonte du site Internet d'une entreprise commerciale, l'objectif est multiple :

  • toucher sur la toile les publics que cherche à prospecter la marque,
  • impacter le marché par l'attractivité et l'amplitude des gammes de produits,
  • lier une relation professionnelle avec les réseaux de distribution et centrale de référencement,
  • communiquer ses valeurs et son engagement auprès des clients finaux,
  • contribuer au développement des ventes et des parts de marché.

Convaincu par les résultats très significatifs constatés à la suite des premières actions préconisées par notre agence, Sylvain nous a naturellement confié son projet Web en connaissance de cause.

Drupal pour voir plus loin

À celles et ceux qui imaginent que la puissance de Drupal implique un back office très complexe, la facilité et la rapidité avec lesquelles l'équipe de La Chanteracoise s'est appropriée les outils de publication démontrent qu'il n'en est rien ! Il appartient à l'agence de se mettre un minimum à la place de son client pour :

  • configurer un espace d'administration qui soit à la fois fonctionnel et agréable,
  • faire en sorte que la gestion du contenu soit un plaisir,
  • adapter les accès, les rôles et les droits des utilisateurs aux besoins avérés d'interventions récurrentes sur le contenu.

Côté public, la richesse et la diversité de Drupal et de ses nombreux modules ouvrent la voie toute grande à une ergonomie très favorable pour l'interactivité entre les contenus, l'accessiblité et la navigation mobile.

Bon, assez parlé de communication ! Envie de faire croustiller vos petits déjeuners et vos goûters gourmands ? Direction la-chanteracoise.fr !

Pages