Drupal et les discussions en vrac

J'avoue ignorer la raison originelle de ce problème, mais de temps en temps, lorsque l'on utilise le mode "par discussion" des commentaires, il arrive que l'ordre des interventions ne soit plus respect. Pire, certains commentaires créés par le lien répondre se trouvent rattachés à d'autres commentaires que leur parent d'origine. Voici donc ma méthode pour traiter ces mauvaises herbes.

timestamp et pid versus thread

A l'origine il est possible de classer les commentaires en se basant sur deux informations de la table comments, pid pour l'id du commentaire parent et timestamp pour déterminer la position des commentaires ayant le même parent. Lorsque pid a pour valeur 0, le commentaire a été créé par le lien ajouter un commentaire.

Pour accélérer l'affichage des commentaires en mode "par discussion", un autre champ a été ajouté, renseigné en parallèle des deux champs précédent, le champ thread.

Ce dernier utilise un format spécial appelé vancode (ce nom vient pseudo du développeur Unconed qui a trouvé cet algo lors du Drupalcon de Vancouver en 2006). Le principe du Vancode est expliqué en détail sur la page d'aide de la fonction comment_render. Il s'agit d'utiliser une conversion en base 36 (les 10 chiffres et les 26 lettres donc), pour obtenir une chaîne de caractère qui suffit juste de trier par un order by pour obtenir l'ordre des commentaires.

Ainsi, un thread de commentaires bien formés donnerait ceci

artisan=select cid,timestamp,pid,thread from comments where nid=1736 order by thread;
cid | timestamp | pid | thread
------+------------+------+------------
4412 | 1244281179 | 0 | 01/
4413 | 1244319887 | 0 | 02/
4414 | 1244384280 | 4413 | 02.00/
4419 | 1245064944 | 0 | 2201/
4420 | 1245065769 | 4419 | 2201.00/
4425 | 1245328518 | 0 | 420101/
4427 | 1245332623 | 4425 | 420101.00
root

Premier constat, les vancodes croissent un peu vite. Cela tient à ce qu'un nouveau vancode est calculé sur la base du plus grand des vancodes précédent (vancode=max(thead)+1). Il y a d'ailleurs eu une longue discussion à ce sujet ici. Ceci étant dit, pour des discussions de taille modeste, la taille des vancodes n'est pas problème, et le cas échéant, vous pouvez toujours appliquer le patch donné dans la discussion qui consiste simplement à limiter la recherche du max aux commentaires de la racine (pid=0).

Maintenant ce n'est pas là la cause des discussions en vrac, et très sincèrement je n'ai pas la moindre idée de la raison de ce chaos occasionnel que je l'ai constaté pour 5 discussions sans lien apparent sur Artisan. Peut-être le calcul a changé d'une version à l'autre de Drupal et les mises à jour n'ont pas répercuté cela correctement ?

Anatomie d'un arbre malade

Un exemple d'une discussion bien malade est celui-ci :

artisan=select cid,timestamp,pid,thread from comments where nid=1711 order by thread;
cid | timestamp | pid | thread
------+------------+------+---------------------------
4273 | 1236355514 | 0 | 00/
4321 | 1239376897 | 0 | 00/
4303 | 1238061397 | 0 | 00/
4428 | 1245445615 | 0 | 00/
4282 | 1236630421 | 0 | 00/
4278 | 1236543387 | 0 | 00/
4424 | 1245326121 | 0 | 00/
4423 | 1245283280 | 0 | 00/
4314 | 1238968158 | 4303 | 00.01/
4274 | 1236359269 | 4303 | 00.02/
4426 | 1245332489 | 4424 | 00.03/
3971 | 1231460621 | 0 | 01/
3972 | 1231487065 | 3971 | 01.00/
3973 | 1231497725 | 0 | 2101/
3996 | 1232181501 | 3973 | 2101.00/
3974 | 1231520127 | 0 | 2102/
3997 | 1232181745 | 3974 | 2102.00/
4255 | 1236017663 | 0 | 410201/
4256 | 1236027060 | 4255 | 410201.00/
4258 | 1236027360 | 4256 | 410201.00.00/
4269 | 1236288975 | 4258 | 410201.00.00.00/
4270 | 1236292663 | 4269 | 410201.00.00.00.00/
4271 | 1236297541 | 4270 | 410201.00.00.00.00.00/
4272 | 1236322645 | 4271 | 410201.00.00.00.00.00.00/
root
exemple de thread malade

On y remarque déjà que le vancode n'évolue pas d'un commentaire "racine" à l'autre, ayant pour résultat un affichage en vrac. Mais plus grave, et conséquence logique du premier problème, on constate aussi par exemple le commentaire cid:4426, qui a comme parent officiel pid:4424 est du coup rattaché (par le champ thread) au commentaire 4423.

Maintenant la bonne nouvelle est que les pid et les timestamp, eux, sont juste. Il est donc possible de reconstruire les champ thread pour corriger le problème.

Traitement de l'arbre malade

Pour effectuer cette correction, il va falloir écrire une petite procédure PHP que l'on lancera de manière autonome soit par un fichier PHP à la racine de Drupal, avec le bootstrap qui va bien (attention à bien protéger ce genre de fichier !!) ou, plus propre, rattacher cette fonction à un hook_menu dans un module perso, et le lancer par une URL du type /corriger_thead/, où est l'id du papier dont le thread de commentaires est en vrac.

Pour cet article, je vais prendre l'option la plus simple, le fichier autonome, mais sachez que c'est Très MAL car multiplier ainsi les .php à la racine de Drupal, multiple logiquement les failles possible sur votre site. Là c'est juste pour l'exemple.

<?php
// Inclusion du bootstrap drupal
require_once './includes/bootstrap.inc';

// Démarrage de Drpal
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

 Si ce n'est pas l'administrateur qui lance ce script, on arrête tout
global $user;
if ($user->uid!=1) {
  drupal_access_denied();
  exit;
}

/** Fonction de correct d'un thread
 * @param $nid L'id du node à corriger
 */

function corrige_thread($nid, $thread, $pid) {
  // Récupération de commentaires pour le node $nid, avec comme commentaire parent $pid
  $cursor = db_query("select cid,thread from comments where nid=%d and pid=%d order by timestamp", $nid, $pid);

  // Initialisation du vancode
  $current = int2vancode(0);
  while ($comment = db_fetch_object($cursor)) {
    // construction de la chaîne 'thread'
    if ($thread<>'') {
      $new_thread = $thread . "." . $current;
    } else {
      $new_thread = $current;
    }
    // Mise à jour du champ 'thread'
    db_query("update comments set thread='%s' where cid=%d", $new_thread . "/", $comment->cid);
    print "".str_pad($comment->cid, 6, '0',STR_PAD_LEFT).
      " (".str_pad($comment->pid, 6, '0',STR_PAD_LEFT).") @ ".
        $comment->timestamp." : ".str_pad($comment->thread,30)." --&gt; ".$new_thread."\\n";

    // Correction des sous-commentaires
    corrige_thread($nid, $new_thread, $comment->cid);

    // Incrémentation du vancode
    $current = int2vancode(vancode2int($current) + 1);
  }
} // fin corrige_thread

// récupération du nid à corriger en argument
$nid=arg(0);

// Lancement de la correction
print "<pre>";
corrige_thread($nid, '', 0);
print "</pre>";

// fin du traitement
script de correction d'un thread

Rien d'extravagant dans ce code qui se base simplement sur un traitement récursif de l'arbre de discussion et renumérote les vancodes en dans l'ordre du champ timestamp. Il s'exécute par http://www.mon_site/corriger_thread.php?q= avec pour résultat le statut des corrections :

003971 (000000) @  : 01/                            --> 00
003972 (000000) @  : 01.00/                         --> 00.00
003973 (000000) @  : 2101/                          --> 01
003996 (000000) @  : 2101.00/                       --> 01.00
003974 (000000) @  : 2102/                          --> 02
003997 (000000) @  : 2102.00/                       --> 02.00
004255 (000000) @  : 410201/                        --> 03
004256 (000000) @  : 410201.00/                     --> 03.00
004258 (000000) @  : 410201.00.00/                  --> 03.00.00
004269 (000000) @  : 410201.00.00.00/               --> 03.00.00.00
004270 (000000) @  : 410201.00.00.00.00/            --> 03.00.00.00.00
004271 (000000) @  : 410201.00.00.00.00.00/         --> 03.00.00.00.00.00
004272 (000000) @  : 410201.00.00.00.00.00.00/      --> 03.00.00.00.00.00.00
004273 (000000) @  : 00/                            --> 04
004278 (000000) @  : 00/                            --> 05
004282 (000000) @  : 00/                            --> 06
004303 (000000) @  : 00/                            --> 07
004274 (000000) @  : 00.02/                         --> 07.00
004314 (000000) @  : 00.01/                         --> 07.01
004321 (000000) @  : 00/                            --> 08
004423 (000000) @  : 00/                            --> 09
004424 (000000) @  : 00/                            --> 0a
004426 (000000) @  : 00.03/                         --> 0a.00
004428 (000000) @  : 00/                            --> 0b
résultat d'exécution

Comme vous le voyez, toutes les erreurs sont maintenant corrigées et la discussion s'affiche maintenant de manière propre.

Conclusion

Je serais tout de même curieux de connaître l'origine exacte de ce problème même si la piste de la mise à jour foireuse semble la plus probable, ou peut-être un problème spécifiquement lié à l'utilisation de PostgreSQL... Toujours est-il qu'une fois traités, l'arbre ne semble plus incliné à une nouvelle dégénérescence.

Enfin notez l'existence du module Comment Mover qui permet de faire de l'arboriculture de discussions de manière ponctuelle et précise. Le nouveau mainteneur précise qu'il ne faut pas l'utiliser en production mais je n'ai pour l'instant jamais eu de problème avec et il se révèle bien pratique pour gérer les contributeurs qui refusent obstinément d'utiliser le bouton répondre :)

Vus : 358
Publié par arNuméral : 54