Mes nouvelles activités ne me laissent que peu de temps pour mettre à jour ce blog. En attendant une éventuelle reprise de mes divagations éditoriales, vous pouvez toujours parcourir les anciens articles. A+ et bonne lecture!
Inéation
100% Drupal : Actualité, Documentation, Formation, Module, Thème
Thèmes Drupal : un style différent pour chaque section de votre site
Niveau : intermédiaire à avancé
Imaginons un site fictif structuré en trois sections:
- produit (www.monsite.fr/produit/*),
- services (www.monsite.fr/services/*),
- blog (www.monsite.fr/blog/*),
La mise en page de chaque section est identique mais afin de renforcer leur identité propre, chaque section comportera un header d'une couleur différente.
Comment faire cela ?
Comme souvent il existe plusieurs solutions. Dans cet article, nous allons utiliser une solution retenue par l'excellent Basic, un starter thème dont je vous ai vanté les mérites dans un précédent article.
Nous allons ajouter à la balise de chaque page du site une classe CSS qui sera générée en fonction de l'url du site. Ainsi, par exemple, lorsque nous serons sur la page d'un produit, nous aurons:
Ensuite, dans la feuille de style, il suffira de cibler le header avec une règle différente pour chaque section:
/* Le header s'affiche en gris par défaut */
header{
background-color: #ccc;
}
/* Le header s'affiche en rouge dans la section produit */
.section-produit #header {
background: #F00;
}
/* Le header s'affiche en vert dans la section services */
.section-services #header {
background: #0F0;
}
/* Le header s'affiche en bleu dans la section blog */
.section-blog #header {
background: #00F;
}
Comment générer la classe ?
C'est ici que cela se complique, enfin juste un petit peu.
Drupal 6 dispose en standard d'un variable appelée body_classes. Cette variable est utilisée pour stocker toutes les classes dynamiques de la page.
C'est ainsi que l'on y trouve les classes suivantes : front ou not-front, logged-in ou not-logged-in, etc... Très utiles, je vous invite à découvrir toutes ces variables.
On retrouve cette variable dans page.tpl.php :
">
Ces variables sont calculées et définies par Drupal juste avant le traitement de page.tpl.php, le template qui permettra de générer le code html de la page. Ce travail c'est l'œuvre d'une fonction importante appelée template_preprocess_page.
OK, on sait maintenant où sont calculées les valeurs stockées dans body_classes[]. Maintenant comment va-t-on y ajouter nos propres classes ? Tout simplement en créant une fonction de thème "phptemplate_preprocess_page()" qui va être insérée dans le fichier template.php de votre thème. Cette fonction vous permet de rajouter toutes les variables que vous souhaitez rendre disponibles à Drupal lors du traitement de page.tpl.php. A l'inverse de "template_preprocess_page" elle est placée dans votre répertoire de thème et est donc librement éditable.
Allez donc faire un tour dans template.php (créez le si nécessaire), vérifiez que la fonction n'existe pas et insérez les deux fonctions suivantes :
function phptemplate_preprocess_page(&$vars, $hook) {
global $theme;
// Classes for body element. Allows advanced theming based on context
// (home page, node of certain type, etc.)
$body_classes = array($vars['body_classes']);
if (user_access('administer blocks')) {
$body_classes[] = 'admin';
}
if (!$vars['is_front']) {
// Add unique classes for each page and website section
$path = drupal_get_path_alias($_GET['q']);
list($section, ) = explode('/', $path, 2);
$body_classes[] = phptemplate_id_safe('page-'. $path);
$body_classes[] = phptemplate_id_safe('section-'. $section);
if (arg(0) == 'node') {
if (arg(1) == 'add') {
if ($section == 'node') {
array_pop($body_classes); // Remove 'section-node'
}
$body_classes[] = 'section-node-add'; // Add 'section-node-add'
}
elseif (is_numeric(arg(1)) && (arg(2) == 'edit' || arg(2) == 'delete')) {
if ($section == 'node') {
array_pop($body_classes); // Remove 'section-node'
}
$body_classes[] = 'section-node-'. arg(2); // Add 'section-node-edit' or 'section-node-delete'
}
}
}
$vars['body_classes'] = implode(' ', $body_classes); // Concatenate with spaces
}
function phptemplate_id_safe($string) {
// Replace with dashes anything that isn't A-Z, numbers, dashes, or underscores.
$string = strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string));
// If the first character is not a-z, add 'n' in front.
if (!ctype_lower($string{0})) { // Don't use ctype_alpha since its locale aware.
$string = 'id'. $string;
}
return $string;
}
Je vous laisse investiguer et comprendre le détail car cette fonction fait un peu plus que nécessaire pour cette exercice. En bref, la fonction récupère l'url utilisée (l'alias pas l'url interne c'est ça qui est fort) pour la transformer en une chaine de caractère. C'est cette chaine qui est utilisée pour nommer la classe en "section-nom_de_la_section". Puis elle est stockée dans $body_classes[].
Et voila, lors de l'affichage d'une page, Drupal 6 va lancer la fonction et créer toutes les classes correspondantes pour les injecter dans la balise.
Exemple:
Si nous sommes dans la section blog, la chaine "section-blog" sera générée par phptemplate_preprocess_page() , stockée dans body_classes[], puis injectée dans page.tpl.php pour que Drupal puisse envoyer au navigateur le code HTML de la page. A ce moment vos CSS prendront le relai et feront afficher le header en bleu.
Vous passez dans la section Services ? Et hop, le header s'affiche en vert.
Victoire ! Non seulement vous savez modifier vos pages en fonction de leur alias mais en plus vous en savez un peu plus sur le système de thème de Drupal...
Qu'en pensez vous ? L'article est-il suffisamment clair ou faut-il préciser certains points ?



Une précision, cet article est à but pédagogique, il permet de se plonger dans le système de thème de Drupal...
Pour les flemmards, deux solutions :
* Utiliser un thème comme "basic "qui a cette fonction en standard,
* Utiliser un module comme "section", http://drupal.org/project/sections, qui permet de spécifier un style, voir un thème différent pour chaque section
Bref, plein de solutions pour tous les gouts !
Merci Alexandre pour cet article, Drupal n'est pas fait pour les flemmards du moins si l'on veut obtenir un résultat satisfaisant. sinon j'ai remarqué qu'il y a très peu de communauté françaises qui traitent de drupal (pour le maroc ca faut même pas rêver).
Merci pour ton commentaire.
D'après moi, Drupal est utilisable à plusieurs niveaux, dont un accessible sans écrire une ligne de code, voir mon article sur Ineaguide. Après bien sur, mais cela est vrai pour n'importe quel outil, il faut de la patience et de la persévérance pour en sortir une belle réalisation.
Quand à certaines communautés francophones hors de France, le problème tient à la taille critique des utilisateurs dans chaque pays. Ceci dit, si tu penses pouvoir organiser qlq chose, la toute nouvelle association Drupal France et Francophonie devrait pouvoir te donner un coup de main. Mais là on dérive du sujet de cet article.
Juste pour compléter, il y a aussi l'approche de la sélection automatique de template. Sur une URL de type "www.monsite.fr/produit/12/afficher" pour reprendre ton exemple, phpEngine est plutôt bien fichu et va chercher dans l'ordre les templates :
1. page-produit-12-afficher.tpl.php
2. page-produit-afficher.tpl.php
3. page-produit.tpl.php
4. page.tpl.php
C'est le même principe que pour le node type, pour un ndoe de type "story", il cherchera :
1. node-story.tpl.php
2. node.tpl.php
Ca ne remplace pas ta technique qui est la seule efficace dés qu'il s'agit d'injecter des variables dans un template mais l'approche nommage est pas mal non plus et très facile à mettre en oeuvre par nos amis graphistes.
Sinon la liste exhaustive est ici :
http://drupal.org/node/190815
Effectivement, le cycle de suggestion pour page.tpl.php est très complet, mais à ma connaissance cela ne marche que pour l'url interne de Drupal, pas pour les alias définis par exemple avec pathauto. Ainsi dans l'exemple que tu cites "produit" ne peut pas être un alias...
"The suggestions are numerous. The one that takes precedence is for the front page. The rest are based on the internal path of the current page. Do not confuse the internal path to path aliases which are not accounted for. Keep in mind that the commonly used Path auto module works its magic through path aliases."
Qu'en penses tu ?
Intéressant, j'avoue que je ne m'étais jamais posé la question car j'utilise peu ou pas le module alias dans ce cas de figure. Soit je passe par la taxo et la chaîne de tpl fonctionne avec, soit je fabrique un petit module custom qui ne contient un hook_menu dont le seul rôle est de virer la catégorie (ex. produites/node/12 => /node/12) et de router vers le call back classique du node.
Je suis justement en train de voir cela avec le site de mediapart qui a les deux cas de figure. Des sections taxonomesques (internat, france, etc.), et des "sous-site" (club, journal, etc.)
Une solution d'une logique très drupalienne donc intéressante pour cela.
En revanche à bien considérer cette solution, elle est assez effrayante : ça fait quand même un gros pavé de code php assez obscur pour pouvoir générer une classe css en php en fonction d'une rubrique.
Je serai curieux de voir une autre solution pour résoudre ce besoin :-)
Je ne voulais pas effrayer ;-). Mais je te remercie pour ton commentaire. Je crois que je vais changer mon exemple qui est trop simpliste et donc la solution un "overkill" par rapport au problème.
Mais effectivement cela permet de mieux comprendre le système de thème de Drupal.
Juste quelques précisions :
Pour finir: le système de thème de Drupal n'est pas si compliqué. Une fois que l'on a compris le concept (cela n'est pas l'objet de cet article) on s'aperçoit que tout est possible.
Pour ceux que cela intéresse, j'en profite pour faire un peu de pub, je suis entrain de préparer une formation de 2 jours dont le but est de permettre de (vraiment) comprendre ce concept, de (bien) comprendre les mécanismes sous-jacent au système de thème afin d'avoir toutes les clés pour réaliser des thèmes qui sortent de l'ordinaire.
Salut, oui désolé pour mon raccourci du mot "rubrique". Encore une fois la solution proposée est intéressante car on est en plein dans la logique du framework de drupal.
Pour le systeme de theme, il est vrai qu'une fois qu'on a compris la logique sous-jacente tout s'éclaire; le framework de drupal est un langage vraiment particulier qui demande juste un temps pour se familiariser. Surtout quand comme moi on a une aversion naturel pour les arrays ^^
Ya pas de problème, tes commentaires et ceux des autres ont une véritable valeur. Ils apportent un éclairage différent aux aspects traités dans cet article.
Bref je ne suis que trop heureux de te voir réagir. Et moi aussi j'aime pas les arrays ;-)
Bonjour,
Un grand merci pour nous faire partager ces astuces, n'étant pas très calé côté PHP, j'ai toujours eu du mal a modifier a ma guise un thème drupal.
Seulement après avoir suivit à la lettre le tutoriel du dessus, je ne suis pas parvenu a avoir le changement de class du body fonctionnel.
le body prend la classe:
à la place de l'alias de la rubrique.J'ai du créer le template.php car il n'existait pas.
ouvert des balises PHP et placé les deux fonctions donnés plus haut.
Je ne vois pas où est mon erreur, pourriez vous m'aider?
Merci.
La fonction marche correctement puisque vous avez la classe "page-image" qui apparait. Quelle est l'url en question de la page ?
Avez-vous essayé en créant un noeud test et en lui donnant l'alias suivant masection/letitredunoeud (avec Pathauto).
Normalement vous devez alors avoir : page-masection-letitredunoeud et section-masection .
Bon, vu que tu avais l'air de douter de mon approche, j'ai fait un petit test "standalone":
Dans le hook_menu d'un module je rajoute :
$items['rubrique'] = array (
'type' => MENU_CALLBACK,
'page callback' => 'tutoriel_menus_rubrique_router',
'page arguments' => array (),
'access arguments' => array (
'access content'
),
);
Puis la callback :
function tutoriel_menus_rubrique_router() {
$args=func_get_args();
$path=implode("/",$args);
$return = menu_execute_active_handler($path);
return $return;
}
Du coup /rubrique/xxxx est routé en interne vers /xxxx. Mais comme le theme page n'est appliqué qu'au retour de la callback, phpEngine va lui travailler avec le chemin d'orgine. Du coup, il suffit d'ajouter au thème un page-rubrique.tpl.php, faire un coup de rebuild du cache de thème, et c'est gagné.
Il est aussi possible de faire des choses plus génériques encore en utilisant un tableau de rubriques, une boucle de création d'items qui passent le nom de la rubrique en premier paramètre.
Voilà, en espérant que cela serve
Excellent ! Merci de partager ton astuce.
Bravo, et non je ne me serai pas permis de douter de toi ;-)
Oups, alors mon problème viendrait de l'url...
En fait je n'ai fait les tests que sur des url simple, de type:
monsite.com/front, monsite.com/photos.
Cette astuce ne marcherait elle que sur des url a plusieurs niveaux de type: monsite.com/masection/letitredunoeud ?
c'est un site simple, les url ne contiennent qu'un seul niveau.
Merci pour la réponse.
Ok ok j'arrête de me la jouer paranoïaque ;-) Content que cela puisse t'intéresser en tout cas.
Petite précision, il est aussi possible de faire soit-même, dans la callback appel à la fonction theme('page',...) ce qui peut avoir comme avantage d'injecter des variables en place avec notamment le nom de la rubrique (via arg(0)), et donc de faire un peu la même chose que toi en jouant sur la classe.
bref, une fois de plus, Drupal est une vraie pâte à modeler. Faudrait tenter de faire la même chose avec Joomla pour rire ;-)
Et qui veulent changer juste quelques couleurs/styles d'une section à l'autre, il existe Css Injector.
Ou et quand ta formation de 2 jours ?
Noémie
Merci pour ton post, il m'aide vraiment.
LE module section fonctionne très bien,je le recommande !
Attention, les balises code ont un gros problème dans ce tuto, c'est tout chamboulé !
En effet, dans le magnifique tuto d'Alexandre, en tout début de tuto, pas mal de balises code ont été employée sans que le contenu ne soit rendu fidèlement. :( Cela n'est pas totalement indispensable à sa compréhension mais pourrait y contribuer aisément je pense.
PS: Salut à tous, et merci à tout un chacun pour le travail que vous faites pour nous permettre d'évoluer dans cet univers.
Salut à tous,
Merci encore pour ce superbe tuto, cela dis j'ignore sur laquelle des balise ces modification sont appliquée. Logiquement je me dis que c'est sur le header vue que c'est lui qui change de couleur suivant les sections sur lesquelles nous pouvons nous trouver. Merci de bien vouloir revoir l'affichage des contenus des balises "code" je vous prie Merci beaucoup, ça pourrais beaucoup nous servir.
ou est quand cette formation de 2 jours stp.