Personnalisation par profils avec SPIP

Comment utiliser l’authentification dans l’espace public pour proposer un site personnalisé selon les données de l’utilisateur

Profils et personnalisation

L’idée est d’être capable de proposer au visiteur un site spécifiquement organisé et habillé selon ses préférences. Par exemple, si dans un site il y a de l’information régionale, le visiteur pourra dire une fois pour toutes quelle est sa région, et alors, à chaque fois qu’il visitera le site, seront mises en avant les informations qui concernent sa région.

L’utilisateur pourrait aussi, par exemple, choisir parmi plusieurs «skins», habillages graphiques d’un site (avec différentes couleurs de fond et d’écriture, les menus à gauche, à droite ou en haut, ...) qui présentent le contenu différemment.

Mais attention à la loi...

On peut imaginer pas mal de choses intéressantes, et c’est une fonctionnalité que les business(wo)men adorent... Mais attention, la base de données des utilisateurs de votre site SPIP contient des données personnelles. Au delà des enjeux techniques, il y a des aspects légaux à considérer: en vertu de législations telle que la directive européenne sur les données personnelles , informatique et liberté ou toute autre applicable là où vous êtes, on ne peut pas faire n’importe quoi avec des données relatives à des êtres humains.

D’ailleurs, la philosophie SPIP étant fort soucieuse de la protection des personnes, c’est aussi pour cela que rien de ce genre n’a jamais été intégré dans son noyau. ...ce qui n’empêche que SPIP est suffisamment versatile pour mettre une personnalisation en oeuvre, et c’est ce que nous allons faire ici!

Que fait l’exemple distribué avec la contrib?

Dans l’exemple, chaque utilisateur peut choisir sa région et une «skin»:
-  sa région peut être «Est» ou «Ouest»,
-  sa skin est «bleu», «vert» ou «arc-en-ciel»

Les valeurs de région et de skin, c’est-à-dire son profil, sont définies dans ses «informations personnelles», modifiables à la même page que le login, le nom, la bio, etc.

Lorsqu’un utilisateur se connecte dans le site public (mais avec login et mot de passe), on utilise ces informations pour lui présenter le site différemment:
-  selon sa skin, on changera les couleurs de présentation sur la page,
-  dans le sommaire, on lui présentera en premier les informations spécifiques à sa région.

Installer les fichiers de l’exemple distribué avec la contrib

Pour mettre en place une personnalisation selon vos besoins, il vous faudra sans doute mettre un peu les mains dans le cambouis, mais d’abord pour mettre les idées au clair et étudier comment procéder dans un cas réel, une bonne méthode est déjà d’installer rapidement l’exemple distribué.

Pour aller au plus vite, nous vous suggérons de partir d’une nouvelle installation et de regarder la base d’articles et utilisateurs fournie en exemple.

-  Installer un site SPIP, [1],
-  télécharger le fichier joint à cette contrib profils.zip :

-  le décompresser,
-  Copier tous les fichiers décompressés dans les répertoires correspondants de l’installation SPIP [2].
-  connectez vous à l’espace privé, allez dans l’administration et restaurez la base dump.xml.gz distribuée ici.

C’est tout. Cette base contient plusieurs utilisateurs: admin1, admin2, auteur1n auteur2, visiteur (les password sont identiques aux logins), avec des profils différents.

Connectez-vous et déconnectez-vous en les utilisant, vous verrez la personnalisation. Dans l’espace regardez les informations personnelles: les informations de profils y sont.

Si vous voulez, publiez des articles. Tout article qui porte le mot clé «Est» apparaîtra dans le sommaire présenté aux utilisateurs de l’est, et de même pour l’«Ouest».

Un petit bug

Il subsiste un hic dans ces squelettes de personnalisation: lorsque l’utilisateur change les données de son profil, cela ne se répercute pas immédiatement dans sa vue du site public.

Nous verrons plus loin la raison: il faut actualiser quelques fichiers dans le cache.

Afin de pallier à cela on peut imaginer la solution suivante: l’utilisateur modifiera ses données de profil, non pas à travers l’interface privée comme maintenant, mais via une page interactive du site public (qui, lui, actualisera le cache approprié).

A suivre dans les versions ultérieures de cette contrib... propositions bienvenues!

En attendant, mettez un délai raisonnable aux squelettes, et prévenez les utilisateur ;-)

Comment utiliser les fichiers de l’exemple pour mon site?

Faire un site SPIP complet et personnalisé est un travail assez long et minutieux. Si vous avez un site vivant, vous avez sans doute déjà bon nombre de particularités par rapport aux squelettes de distribution. Si votre jeu de squelette est bien architecturé, il vous sera sans doute possible de mettre en œuvre cette contrib, mais il faudra au moins comprendre comment ça marche.

Afin d’utiliser les fichiers de l’exemple proposé pour intégrer de la personnalisation, vous devrez:

  1. définir les profils que vous voudrez gérer. Comme mentionné, dans l’exemple, chaque utilisateur peut choisir sa région et une «skin».
  2. décider comment vous allez gérer les critères de profil. Basiquement, on définit ici deux manières de gérer les profils [3]:
    1. en PHP: le critère de profil est transmis au squelette comme une variable dans l’URL, et on construit du PHP dans le squelette pour personnaliser en fonction de la valeur. Dans l’exemple, on choisit ainsi la feuille de style que l’on applique à la page.
    2. avec un squelette par profil: la valeur du critère est aussi transmise comme une variable dans l’URL, mais ici le .php3 du squelette choisit un fond différent (le .html du squelette) pour chacune des possibilités de profils. Dans les fichiers d’exemple, on traite ainsi deux régions différentes, et une version du site pour ceux qui n’ont pas de région.
  3. configurer vos champs #EXTRA pour vos profils,
  4. Configurer vos fichiers de construction des profils: sommaire.html, rubrique.html, articles.html et autres,
  5. faire vos squelettes pour les profils, les tester.
  6. tester la personnalisation.

Il vous faut pour cela vous plonger un peut dans la compréhension de l’exemple.

Explication des fichiers de l’exemple

Premier mécanisme: choix du profil pour un utilisateur logué

Le profil de chaque utilisateur est donc défini dans la base des utilisateurs.

On pourrait détourner un champs existant de la table des utilisateurs si on ne l’utilise pas, mais pour être élégant ici on va utiliser les champs #EXTRA. On configure deux champs qui s’appellent region et «skin» en ajoutant les lignes suivantes dans le fichier [4] mes_options.php3:

<?php
// lignes à ajouter dans ecrire/mes_options.php3
//////////////////////////////////////////////////
GLOBALS['champs_extra'] = Array(
   'auteurs'        => Array(
     'region' => 'ligne|propre|Région (est, ouest)',
      'skin'        => 'ligne|propre|Skin (bleu, vert, rainbow)',
    ),
);
//////////////////////////////////////////////////
?>

Pour les visiteurs, on peut développer un squelette qui assure cette saisie. A travers l’interface privée de SPIP, cependant, un rédacteurs devra saisir exactement le nom du profil qu’il souhaite. À l’avenir, si on dispose de champs «radio» ou listes déroulantes, on pourra proposer les valeurs exactes des profils existants aux utilisateurs [5].

Considérons un squelette, que ce soit de sommaire, d’article ou de rubrique. La première chose est de d’utiliser la balise #LOGIN_PUBLIC pour proposer au visiteur de se loguer s’il ne l’est pas déjà, tel que documenté dans la doc officielle. S’il l’est, on le détecte par l’existence de la variable PHP $auteur_session. Pour les visiteurs non authentifiés, on affiche la version publique du site:

<?php
// ceci est le contenu du fichier sommaire.html

// d'abord on verifie si pn est deja logue, et on affiche l'authent et espace public.
        if (!$auteur_session) {
?>
#LOGIN_PUBLIC
<INCLURE(sommaire_public.php3)>
<?php
        } else {
 
    .... et ici on va essayer de faire de la personnalisation...
        }
?>

Si l’utilisateur est logué, on dispose dans de son id_auteur dans l’élément de tableau $auteur_session['id_auteur']. On va l’utiliser pour appeler un squelette qui dépend de cette donnée. Une variable php ne pouvant être appelée dans une instruction SPIP, l’astuce consiste à changer d’URL en PHP, et y ajouter une variable bien connue du contexte SPIP: id_auteur.

Ceci s’écrit en “PHP expliqué”:

<?php
     ........
        header("Location: <nom_du_squelette.php3>?"
                  .["<la_requete_de_l_appel_http>&"]
                 ."id_auteur="
                 .$auteur_session['id_auteur']);
     ........
?>

On passera donc dans un squelette SPIP, appelé avec une adresse du type:

http://monsite.org/squelette_auteur_profil.php3?id_auteur=XX

où XX est le numéro de l'auteur logué.

Dans ce squelette, on peut exécuter une boucle AUTEURS pour extraire les données de l’auteur, en particulier celles de son profil, et on redirige de la même manière vers un squelette mais en passant les critères de profil en variable.

<?php
        <BOUCLE_profil(AUTEURS){tout}{id_auteur}>
                // #NOM
                // On retourne le nom pour debug.
                $region = '[(#EXTRA|extra{"region"})]';
                $skin = '[(#EXTRA|extra{"skin"})]';
                header("Location: <squelette_profils.php3>?"
                                 ["<la_requette_sans_id_auteur>&"]
                                ."region=".$region
                                ."&skin=".$skin);
        </BOUCLE_profil>

Et à ce moment là, on se retrouve dans un squelette où on dispose des variables de profil dans l’URL. On peut faire ce que l’on veut avec en PHP et, en SPIP, on peut les utiliser pour inclure des squelettes où on passe de nouveau ces variables en URL:

        <INCLURE(sommaire_profils.php3){region}{skin}>

Une première astuce: pour ne pas créer pléthore de squelettes dans lesquels on ne se retrouve plus, on peut regrouper ces trois squelettes de redirection vers le profil en un seul: sommaire.html, rubrique.html, article.html, ... selon le cas.

Et deux autres pour assainir notre URL:

-  premièrement, on ne veut pas que les pages envoyées restent dans le cache du navigateur ou d’un proxy: c’est sain dans n’importe quelle application qui est pilotée par le serveur. Ici, le squelette de redirection s’appelant lui même,si l’URL ne change pas, il faut que le navigateur provoque tout de même un calcul PHP sur le serveur (mais pas un recalcule du cache SPIP). On ajoute les entêtes HTTP:

header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);

-  deuxièmement, les critères de profil ne doivent être appelés que par les utilisateurs logués. Si $auteur_session n’est pas défini, on vérifie les variables d’URL et on redirige:

if (isset($region) || isset($skin) ) {
	$requete =  ereg_replace("region=[^&=]*[&=]?","",$_SERVER["QUERY_STRING"] );
	$requete =  ereg_replace("skin=[^&=]*[&=]?","",$requete );
	$requete =  ereg_replace("[&]?$","",$requete );
			header("Location: ".$_SERVER["SCRIPT_NAME"].($requete == ""?"":"?".$requete));
			
}
</code>
Voici le code complet du squelette de personnalisation <code>sommaire.html</code>:

<code>
<?php
// On ne veut pas de ces pages en cache navigateur
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);

// d abord on verifie si on est deja logue,
// et on affiche l authentification et l espace public.
        if (!$auteur_session) {
		if (isset($region) || isset($skin) ) {
			$requete =  ereg_replace("region=[^&=]*[&=]?","",$_SERVER["QUERY_STRING"] );
			$requete =  ereg_replace("skin=[^&=]*[&=]?","",$requete );
			$requete =  ereg_replace("[&]?$","",$requete );
			header("Location: ".$_SERVER["SCRIPT_NAME"].($requete == ""?"":"?".$requete));
			
		}
?>
#LOGIN_PUBLIC
<INCLURE(sommaire_public.php3)>
       
<?php
// Sinon, on regarde si les variables de redirection sont placees et,
// et on redirige avec les variables. La raison d une double redirection,
// d abord par auteur ensuite par profil est pour minimiser le nombre de
// caches de contenu: un par profil, pas un par auteur.
        } elseif  (!isset($id_auteur) && (!isset($region) || !isset($skin)) ) {
		// On doit definir id_auteur et redirige 

		header("Location: ".$_SERVER["SCRIPT_NAME"]."?"
                         .(($_SERVER["QUERY_STRING"]!="")?
                             $_SERVER["QUERY_STRING"]."&":"")
                         ."id_auteur="
                         .$auteur_session['id_auteur']);

        } else {
                if ( !isset($region) || !isset($skin)  ) {
		// avec l id_auteur on cherche en SPIP le profil
		
                        <BOUCLE_profil(AUTEURS){tout}{id_auteur}>
                                // #NOM
                                // On retourne le nom pour debug.
                                $region = '[(#EXTRA|extra{"region"})]';
                                $skin = '[(#EXTRA|extra{"skin"})]';

                                switch (strtolower ($region)) {
                                case "est":
                                case "east":
				case "este":
                                        break;
                                        $region = "est";                                
				case "ouest":
				case "west":
				case "oeste":
                                        $region = "ouest";				
                                        break;
                                default:
                                        $region = "defaut";
                                }
                                switch (strtolower ($skin)) {
                                case "bleu":
                                case "blue":
                                case "azul":
					$skin = "bleu";
                                        break;
                                case "vert":
                                case "green":
                                case "verde":
					$skin = "vert";
                                        break;
                                case "rainbow":
                                case "arc en ciel":
                                case "arc-en-ciel":
                                case "arc_en_ciel":
                                case "arcoiris":
					$skin = "rainbow";
                                        break;
                                default:
                                        $skin = "";
                                }
                                $requete =  ereg_replace("(id_auteur=[0-9]*)","region=".$region
                                                        ."&skin=".$skin,
                                                        $_SERVER["QUERY_STRING"] );
                                header("Location: ".$_SERVER["SCRIPT_NAME"]."?".$requete);

                        </BOUCLE_profil>
                } else {
		// la on a deja les infos de profil. on va chercher le squelette de profils
?>
<INCLURE(sommaire_profils.php3){region}{skin}>
<?php
                }
        }
?>

Je vous laisse vour reporter au manuel PHP si besoin pour intpréter ce script PHP.

Voilà, ce premier squelette complet, nous permet de détecter si l’utilisateur est logué, et lorsqu’il l’est, le rediriger vers un squelette sous la forme:

http://monsite.org/sommaire_profils.php3?region=XXX&skin=YYY

où XXX «est» est ou «ouest», et skin «bleu», «vert» ou «rainbow», ou leurs traductions.

Remarquons que pour obtenir les squelettes d’articles et de rubrique, il suffit de remplacer les deux occurrences de «sommaire» dans l’inclusion de squelettes et d’ajouter à ces mêmes lignes le critère approprié, id_rubrique ou id_article. C’est tout.

Certes, la formulaire de login n’a pas de présentation et se voit très austère. Dans un site réel, pour le mettre en page, il faudra mélanger un peu le squelette initial sommaire.html et le squelette public qui est inclus dans la page sommaire_public.html. Nous vous laissons le soin de le faire, ici, pour la clarté pédagogique de l’exposé, on sépare login et redirection d’un côté, et sommaire public de l’autre.

Deuxième mécanisme: personnalisation du site

Il nous reste à voir comment on traite la personnalisation (par profils) dans sommaire_profils.html.

Il y a sans doute de nombreuses techniques, et on pourra s’inspirer de bon nombre de contribs. Dans l’exemple on en présente deux:
-  du traitement PHP directement dans le squelette (interprété a posteriori, à chaque appel au squelette calculé du cache.
-  le choix d’un squelette différent par valeur possible d’un critère de profil, solution qui est largement inspiré de l’astucieuse
contrib «Choisir son squelette avec un mot clé.».

La première est assez simple. Elle peut être presque invisible. Par exemple on ajoute le nom de la skin lorsqu’on charge la feuille de style. Dans sommaire_profis.html et d’autres:

 
<link rel="stylesheet" href="habillage<?php echo ($skin?'-'.$skin:''); ?>.css" type="text/css" media="print, projection, screen, tv">

On fait aussi de la personnalisation lorsqu’on mentionne à l’écran le nom de la région:

<li class="menu-item">
        <b> <?php echo ($region?"R&eacute;gion ".$region:'(définir region)'); ?></b> - 
        <a href='#URL_LOGOUT'>Déconnexion</a>
</li>

La deuxième inclut un autre squelette et lui passe le critère en paramètre. Comme dans la contrib citée, le fichier .php3 du squelette inclus définit sa variable $fond en utilisant la variable PHP obtenue en URL, et charge un ficher .html différent pour chacune de ses valeurs.

Par exemple, pour chaque région, on veut présenter un encadré particulier. On crée un squelette sommaire_region-<region>.html par région et un pour ceux qui ne précisent pas leur région sommaire_region-defaut.html

Ensuite, dans le fichier <code>sommaire_regions.php3

(attention au pluriel de régions) commun à tous ces squelettes, on trouve:
<?php
$fond 
"sommaire_region-";

//on analyse la valeur de la variable $region
//on construit $fond en fonction de $region
if(isset($region) && $region != ""){
      
$fond .= $region;
}else{
      
$fond .= "defaut";
}

$delais 3600;

include (
"inc-public.php3");

?>

Avec un appel <INCLURE(sommaire_regions.php3){region}> dans le squelette sommaire_profils.html on obtient pour chaque utilisateur le squelette approprié.

Un autre point important de l’architecture proposée

L’essentiel du code a été expliqué ci-dessus. Mais on peut ajouter un un point qui n’a pas été explicitement expliqué: la raison de double redirection dans le fichier de profil. Dans sommaire.html on fait:
-  d’abord une redirection avec une URL qui contient le id_auteur=AAA,
-  ensuite on recommence en enlevant id_auteur et en ajoutant le profil, region=XXX&skin=YYY.

Pourquoi pas une redirection avec tous les paramètres?

Cela vise à optimiser le cache. Rappelons nous d’un principe: Une adresse URL, un fichier de cache. SPIP construit ses fichiers de cache avec la requête de l’URL.

Dans notre exemple, on calcule un petit squelette par utilisateur pour le rediriger, mais ensuite, pour les squelettes de contenu, a priori plus gros et complexes, on n’en calcul qu’un pour chaque profil. En mettant un URL qui dépend de id_auteur pour la redirection, mais ensuite seulement du profil pour le contenu, on fait une version de cache par profil, et juste un petit squelette par utilisateur.

Footnotes

[1testé avec la version 1.7.2, et devrait au moins marcher avec les versions supérieures.

[2bien sûr, si vous êtes déjà en train de configurer, non plus une installe neuve mais un site existant, si votre installation comprend déjà des personnalisations avec mes_fonctions.php3 et/ou mes_options.php3, il vous faut copier les lignes des fichiers distribués dans les vôtres.

[3mais on peut en inventer beaucoup d’autres!

[4Ce fichier se trouve dans le dossier ecrire/; s’il n’y est pas créez le.

[5Les champs extra sont en cours de documentation dans spip-contrib . Ici on reste compatible avec la version des champs extra qui est distribuée avec SPIP 1.7.2

updated on 11 February 2007

Discussion

3 discussions

  • Comment peut-on accèder à cette variable $auteur_session dans un script en php indépendant des squelettes ?
    Merci de votre aide.

    Reply to this message

  • JE SUIS NOUVEAU SUR LE SITE SPIP J’AI CREE UN SITE ET J’AIMERAI BIEN CHANGER LE FONT DE MON SITE ES QUELQUN PEUX M’AIDER SVP MERCI

    Reply to this message

  • 4

    Bonjour,

    Bravo pour ton travail.

    Je me de mandais si tu avais eu l’occasion de tester cette contrib avec spip.1.8 ?

    Autre question, y a-t-il un exemple de site en ligne utilisant cette contrib ?

    Meri d’avance et encore bravo.

    @lexandre

    • Merci! ;-)

      Je me demandais si tu avais eu l’occasion de tester cette contrib avec spip.1.8 ?

      Non, elle était faite avec Spip 1.7, et je ne l’ai pas testé avec la 1.8 :-(

      Autre question, y a-t-il un exemple de site en ligne utilisant cette contrib ?

      Non plus :-( j’avais fait un petit site de test, mais je crois que je l’ai scratché. Et je n’ai utilisé cela que dans un intranet ;-(

      N’hésite pas à faire un retour ici si tu l’utilises.

    • Salut et merci pour ta réponse.

      J’ai essayé d’installer ta contrib sur un site spip 1.8.

      Malheureusement, je n’ai pas réussi à importer le fichier dump.xml.gz ...

      Cordialement.

      @lexandre

    • Malheureusement, je n’ai pas réussi à importer le fichier dump.xml.gz ...

      Ca c’est normal: c’est une base créée avec une 1.7.2, il faut l’importer avec la même version.

      Ceci dit, les squelettes eux-mêmes devraient fonctionner sur une 1.8. Deux solutions: soit tu installes une 1.7.2, tu importes la base et tu migres à la 1.8, soit tu installes une 1.8, tu n’utilises que les squelettes et tu recrées des données.

    • Merci pour ta réponse.

      Envisages tu de mettre à jour le fichier dump.xml.gz pour la version 1.8 ?
      Cela pourrait être utile pour les débutants ...

      Merci encore.

      @lexandre

    Reply to this message

Comment on this article

Who are you?
  • [Log in]

To show your avatar with your message, register it first on gravatar.com (free et painless) and don’t forget to indicate your Email addresse here.

Enter your comment here

This form accepts SPIP shortcuts {{bold}} {italic} -*list [text->url] <quote> <code> and HTML code <q> <del> <ins>. To create paragraphs, just leave empty lines.

Add a document

Follow the comments: RSS 2.0 | Atom