Migration vB3 vers Discourse terminée incluant Legacy vB3, merci aux Likes

J’ai presque terminé, pour l’essentiel (encore en phase de test, mais les « parties difficiles » sont essentiellement terminées et la migration fonctionne bien), la migration d’un forum vb3 vieux de 15 ans, qui était à l’origine (croyez-le ou non) un forum vb2 de 2000.

Ce forum existe depuis environ 20 ans (avec du code PHP personnalisé inimaginable, des tables, des plugins, etc.), et Discourse est, à mon avis, le seul logiciel de forum « digne » d’un projet de migration, que j’ai lancé il y a environ une semaine, passant de la faisabilité à l’achèvement (essentiellement).

Tout d’abord, je tiens à remercier toute l’équipe de Discourse pour une telle « œuvre d’art » de forum moderne. Discourse est vraiment remarquable, à mon avis (et je suis sûr que beaucoup d’autres sont du même avis).

Deuxièmement, je tiens à remercier l’équipe qui a codé le script de migration original vbulletin.rb. Ce script était environ 95 % correct. J’ai modifié ce script pour qu’il fonctionne correctement avec vb3. Par exemple, vb3 n’a pas de table filedata comme vb4 (liée aux pièces jointes) ; j’ai donc modifié vbulletin.rb pour qu’il fonctionne. De plus, il y avait des problèmes lors de l’importation des forums enfants vb3 en tant que catégories, mais j’ai modifié le code pour importer tous les forums en tant que catégories de premier niveau, puis j’ai écrit du code postgres psql pour créer de nouvelles relations parent-enfant entre les catégories (ou les configurer manuellement). Il y avait d’autres « pièges » (une histoire pour un autre jour), mais ces gobelins ont rendu l’expérience amusante et stimulante, sans être insurmontable.

Troisièmement, je tiens à remercier Sam et son code lithium.rb, où j’ai utilisé sa routine import_likes comme base pour ma conversion des merci vb3 (d’un plugin hérité, non inclus dans vB OOTB) vers les likes de Discourse. J’ai apporté de légères modifications à la routine import_likes et, après quelques heures de débogage, j’ai réussi à la faire fonctionner.

Comme vous pouvez le voir sur ce post de 2018, migré aujourd’hui :

J’ai jugé important que les merci vb des utilisateurs soient migrés vers les likes de Discourse, et voici donc le code que j’ai utilisé pour migrer les likes pour les autres âmes de forums vb3 perdus :

Tout d’abord, j’ai créé une table mysql user_actions vb3 et j’ai écrit un script PHP pour la remplir à partir de la table héritée vb post_thanks, comme ceci (pas poli, mais fonctionnel) :

ubuntu:/var/www/includes/cron# cat thanks_to_discourse.php
 <?php

/**************************************************
+-----------+------------+-----------+-----------+
| thanker   | unixtime   | id        | thanked   |
+-----------+------------+-----------+-----------+
|         1 | 1584149592 | 303045211 | 302093876 |
| 302153369 | 1584136706 | 303045214 | 302116191 |
| 302108573 | 1584128526 | 303045211 | 302093876 |
| 302153369 | 1584126659 | 303042175 | 302116191 |
| 302153369 | 1584126400 | 303045174 | 302116191 |
| 302153369 | 1584117711 | 303045184 |         1 |
|     37898 | 1584108187 | 303045175 |         1 |
| 302181242 | 1584106664 | 303045201 | 302122047 |
| 302181242 | 1584104642 | 303045074 | 302052697 |
| 302025710 | 1584103722 | 303045184 |         1 |
+-----------+------------+-----------+-----------+
 **************************************************/

$query = 'SELECT p.threadid AS threadid,t.userid AS thanker,t.date AS unixtime,t.postid AS postid,p.userid AS thanked from post_thanks as t LEFT JOIN post p ON p.postid = t.postid';

$allthanks = $vbulletin->db->query_read($query);

/****************************************
mysql> describe user_actions;
+-----------------+------------------+------+-----+---------+----------------+
| Field           | Type             | Null | Key | Default | Extra          |
+-----------------+------------------+------+-----+---------+----------------+
| id              | int(11)          | NO   | PRI | NULL    | auto_increment |
| action_type     | int(11) unsigned | NO   |     | NULL    |                |
| user_id         | int(11) unsigned | NO   |     | NULL    |                |
| target_topic_id | int(11) unsigned | YES  |     | NULL    |                |
| target_post_id  | varchar(16)      | YES  |     | NULL    |                |
| target_user_id  | int(11) unsigned | YES  |     | NULL    |                |
| acting_user_id  | int(11) unsigned | YES  |     | NULL    |                |
| created_at      | timestamp        | YES  |     | NULL    |                |
| updated_at      | timestamp        | YES  |     | NULL    |                |
+-----------------+------------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
 *************************************/

$action_code = 2; // action like de discourse == 2
$target_user_id = "";
while ($action = $vbulletin->db->fetch_array($allthanks)) {

    #code pour gérer la façon dont Discourse identifie le premier post d'un sujet dans la table post_custom_fields
    $query = 'SELECT firstpostid FROM thread WHERE threadid =' . $action['threadid'] . ' LIMIT 1';
    $threadinfo = $vbulletin->db->query_first($query);
    if ($threadinfo['firstpostid'] == $action['postid']) {
        $action['postid'] = 'thread-' . $action['threadid'];
    }

    $update = 'INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, target_user_id,acting_user_id, created_at, updated_at)' .
        ' VALUES (' . $action_code . ',' .
        '"' . $action['thanked'] . '",' .
        '"' . $action['threadid'] . '",' .
        '"' . $action['postid'] . '",' .
        '"' . $target_user_id . '",' .
        '"' . $action['thanker'] . '",' .
        'FROM_UNIXTIME(' . $action['unixtime'] . '),' .
        'FROM_UNIXTIME(' . $action['unixtime'] . '))';
    $doit = $vbulletin->db->query_write($update);
}

Ainsi, cette table mysql user_actions est incluse dans le dump de migration.

Voici la routine import_likes modifiée que j’ai utilisée pour achever la migration du côté de Discourse :

def import_likes
    puts "\nimportation des likes..."

    # création de la table mysql user_actions dans vb3 à l'aide d'un script PHP et inclusion de cette table dans le dump de migration
    sql = "select acting_user_id as user_id, target_post_id as post_id, created_at from user_actions"
    results = mysql_query(sql)
    puts "méthode length() de : #{results.count}\n\n"

    puts "sauter le chargement de la carte d'identifiants uniques"
    existing_map = {}
    PostCustomField.where(name: 'import_id').pluck(:post_id, :value).each do |post_id, import_id|
      existing_map[import_id] = post_id
      #puts "postcustomfield existing_map post_id: #{post_id} import_id #{import_id}\n"
    end

    puts "chargement des données dans la table temporaire"

    # création manuelle de la table temporaire like_data afin de pouvoir vérifier la table après la fin de la session
    #DB.exec("create temp table like_data(user_id integer, post_id integer, created_at timestamp without time zone)")
    
    puts "table temporaire like_data créée"
    PostAction.transaction do
      results.each do |result|

        result["user_id"] = user_id_from_imported_user_id(result["user_id"].to_s)
        result["post_id"] = existing_map[result["post_id"].to_s]

        next unless result["user_id"] && result["post_id"]

        puts "insertion dans la table like user_id: #{result["user_id"]} post_id #{result["post_id"]}\n"

        DB.exec("INSERT INTO like_data VALUES (:user_id,:post_id,:created_at)",
          user_id: result["user_id"],
          post_id: result["post_id"],
          created_at: result["created_at"]
        )

      end
    end


    puts "création des actions de publication manquantes"
    DB.exec <<~SQL

    INSERT INTO post_actions (post_id, user_id, post_action_type_id, created_at, updated_at)
             SELECT l.post_id, l.user_id, 2, l.created_at, l.created_at FROM like_data l
             LEFT JOIN post_actions a ON a.post_id = l.post_id AND l.user_id = a.user_id AND a.post_action_type_id = 2
             WHERE a.id IS NULL
    SQL

    puts "création des actions utilisateur manquantes"
    DB.exec <<~SQL
    INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at)
             SELECT pa.user_id, 1, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
             FROM post_actions pa
             JOIN posts p ON p.id = pa.post_id
             LEFT JOIN user_actions ua ON action_type = 1 AND ua.target_post_id = pa.post_id AND ua.user_id = pa.user_id

             WHERE ua.id IS NULL AND pa.post_action_type_id = 2
    SQL

    # action inverse
    DB.exec <<~SQL
    INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at)
             SELECT p.user_id, 2, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
             FROM post_actions pa
             JOIN posts p ON p.id = pa.post_id
             LEFT JOIN user_actions ua ON action_type = 2 AND ua.target_post_id = pa.post_id AND
                ua.acting_user_id = pa.user_id AND ua.user_id = p.user_id

             WHERE ua.id IS NULL AND pa.post_action_type_id = 2
    SQL
    puts "mise à jour des comptes de likes sur les publications"

    DB.exec <<~SQL
        UPDATE posts SET like_count = coalesce(cnt,0)
                  FROM (
        SELECT post_id, count(*) cnt
        FROM post_actions
        WHERE post_action_type_id = 2 AND deleted_at IS NULL
        GROUP BY post_id
    ) x
    WHERE posts.like_count <> x.cnt AND posts.id = x.post_id

    SQL

    puts "mise à jour des comptes de likes sur les sujets"

    DB.exec <<-SQL
      UPDATE topics SET like_count = coalesce(cnt,0)
      FROM (
        SELECT topic_id, sum(like_count) cnt
        FROM posts
        WHERE deleted_at IS NULL
        GROUP BY topic_id
      ) x
      WHERE topics.like_count <> x.cnt AND topics.id = x.topic_id

    SQL
    end
end

J’espère que ce post aidera d’autres personnes qui pourraient migrer de vB3 vers Discourse, car beaucoup ont utilisé le plugin hérité de remerciements vb3 pendant de nombreuses années.

De plus, j’ai passé beaucoup de temps dans la base de données Discourse et à l’intérieur de nombreux scripts Ruby Discourse, donc cette migration était délicate (pas pour les cœurs faibles assurément), mais certainement réalisable.

En fait, j’ai pris plaisir à le faire. C’était la première fois en Ruby pour moi ; donc c’était amusant de commencer à apprendre Ruby et, en tant que personne MySQL de longue date, la partie Postgres était pour la plupart routinière.

Nous serons en ligne très bientôt… Merci à l’équipe Discourse !!!

Bravo et merci d’avoir partagé !!

Merci @codinghorror

Je viens de repérer un petit gremlin que je vais régler. La routine import_likes ne transfère pas les « likes » sur le premier message (« topic starter ») (seulement les réponses).

Je vais devoir retrousser mes manches, trouver la solution et publier ici la routine import_likes mise à jour.

Il semble que j’aie été un peu trop pressé en publiant, car il reste un petit gobelin à abattre avant de pouvoir déclarer victoire.

Mise à jour :

J’ai ajouté ce code au script PHP de vB3 pour compenser la différence entre la façon dont vB3 et Discourse gèrent le premier message d’un sujet/thread :

    <?php
    # Code vB3 pour gérer la façon dont Discourse identifie le premier message d'un sujet dans la table post_custom_fields
    $query = 'SELECT firstpostid FROM thread WHERE threadid =' . $action['threadid'] . ' LIMIT 1';
    $threadinfo = $vbulletin->db->query_first($query);
    if ($threadinfo['firstpostid'] == $action['postid']) {
        $action['postid'] = 'thread-' . $action['threadid'];
    }

Maintenant, cela fonctionne comme prévu et les « remerciements donnés » dans vB3 sont migrés vers Discourse, y compris le premier message du sujet ; en ajustant la table de migration de vB3 pour qu’elle corresponde à la table post_custom_fields de Discourse.

Je réalise le test final maintenant, mais d’après les tests préliminaires, cela semble fonctionner.

J’ai également mis à jour le code dans mon message initial en ajoutant le fragment PHP ci-dessus.

J’ai lu que vous ne migriez pas les mots de passe car les hachages sont différents. Vous devriez jeter un coup d’œil à mon plugin de migration de mots de passe (ici). Il supportera probablement déjà les hachages VB3, et si ce n’est pas le cas, je suis ouvert à l’ajout de cette fonctionnalité.

Merci Michael,

Nous ne allons pas migrer les mots de passe et préférons que les utilisateurs changent leurs mots de passe (et optent pour des mots de passe plus forts et plus longs).

Merci tout de même pour votre proposition d’aide ! C’est très apprécié.

Nous avons cependant un petit problème sur lequel vous pourriez peut-être nous aider.

Pour une raison inconnue, le nombre total de « likes » reçus par les utilisateurs n’est pas correctement mis à jour dans leurs profils, même si tout le reste semble fonctionner normalement. J’ai essayé de résoudre le problème par moi-même, mais mes tentatives ne m’ont pas donné les résultats escomptés.

Connaissez-vous une bonne méthode pour mettre à jour manuellement ces compteurs de « likes » sur les profils ?

Quelle tâche rake avez-vous utilisée ? Et quel est le problème ? Tous les chiffres sont-ils complètement faux, ou seulement pour certains utilisateurs, ou… ?

Bonjour.

Pour commencer, j’ai essayé la seule tâche Rake qui me semblait logique (à l’époque, puisque le problème est lié à user_actions et post_actions), mais cela n’a rien donné (en fait, en y repensant, j’aurais dû lire cette tâche rake pour voir exactement ce qu’elle fait, LOL) :

rake user_actions:rebuild    

Pour l’instant, franchement, je n’ai pas encore déterminé ce qui ne va pas dans le script de migration import_likes. Si je savais exactement ce qui cloche et où, j’aurais pu le corriger (je passe souvent des heures ou des jours à trouver un problème qui, une fois trouvé, ne prend qu’une minute à corriger… l’essence de la programmation, LOL). J’ai restauré à partir d’une capture plusieurs fois et réexécuté la routine import_likes de différentes manières (ajout de déclarations puts supplémentaires, etc.) et examiné les tables. Les résultats sont restés inconclusifs. Cela est aggravé par mon amateurisme en ruby et en discourse en général ; je ne suis pas encore fluide dans le backend. (Cependant, je m’améliore avec begin... rescue ... end… haha, ce qui est devenu l’un de mes nouveaux outils de débogage en tant que débutant ruby.)

Ce que j’ai observé, c’est que la table temporaire postgres like_data (côté discourse) est correcte et contient toutes les données au bon format, et les entrées correspondent à la fois à la table de migration mysql et au site vB.

Le processus semble échouer lors de la création des tables user_actions et peut-être même post_actions, car mes tests montrent que les données ne sont pas correctement transférées vers ces tables clés.

Veuillez noter que la migration principale vB3 est correcte. Ce problème est lié à un ancien plugin vB3 « merci » que je convertis en likes Discourse. Ma prochaine étape, lorsque j’aurai du temps plus tard aujourd’hui, j’espère, sera de m’asseoir et de voir exactement où le processus échoue lors du transfert de la table like_data (qui est correcte) vers user_actions et post_actions.

Ma conclusion préliminaire, basée sur les tests d’hier, est que le rake ruby ne sera pas d’une grande aide car la rupture se situe dans le processus de mise à jour des tables user_actions ou post_actions ; je dois donc déterminer exactement où se trouve la rupture et pourquoi. Cela pourrait être aussi simple qu’un conflit de type de base de données ou un autre petit gremlin dans le code.

Notre site Discourse n’est pas encore en ligne, donc ce n’est pas une urgence et, comme beaucoup d’ici et ailleurs, des tâches comme gérer les démarches administratives ou bancaires sont devenues plus qu'un peu ennuyeuses, pour ne pas dire plus, et je dois retourner en ville aujourd’hui pour des tâches non liées à Discourse. Je préférerais explorer Discourse et chercher des gremlins de code.

J’ai à nouveau exécuté le script de migration (sans aucune modification du script), y compris le script personnalisé de conversion des balises thanks de vb3 en likes de Discourse (import_likes), et tout semble être OK maintenant.

Je vais essayer de synchroniser le gros « D » avec le dernier dump de la base de données vB et voir ce qui se passe !

Je suis très satisfait de Discourse… Merci encore pour ce forum moderne, véritable « œuvre d’art ». Je continue à tester, mais nous passerons en production bientôt.

5,6 k j’aime donnés… C’est beaucoup d’amour :heart:

Après avoir récupéré tous les vb thanks pour les transférer en discourse likes, voici un exemple de sujet migré datant de l’an 2000, il y a vingt ans, montrant les likes migrés dans le sujet :

… et je commence déjà à avoir hâte d’apprendre à écrire des plugins sympas… :slight_smile:

Très satisfait de Discourse et de l’équipe… et surtout de begin .... rescue ...puts "look at me".... end en Ruby… quel sauveur !

C’est vrai… les vieux chiens peuvent apprendre de nouveaux tours si on leur lance quelques os :slight_smile:

Merci à l’équipe Discourse ! Il y a 20 ans, nous avons lancé un forum pour les utilisateurs Unix et Linux sur vB2, et il y a environ 15 ans, nous sommes « passés » à vB3. Aujourd’hui marque un grand changement pour les utilisateurs Unix et Linux du monde entier et pour nous, car la migration vers Discourse est désormais active.

Merci beaucoup d’avoir créé ce logiciel et de le rendre disponible à tous en tant que projet open source. Votre générosité est très appréciée. À mon avis (et je suis sûr que beaucoup d’autres partagent ce point de vue), Discourse est de loin le meilleur logiciel de forum sur la planète en 2020.

Pour les autres utilisateurs de vB3 legacy qui souhaitent migrer vers Discourse, cette migration n’est pas aussi « simple » que je l’ai présentée. À moins d’être bien versé en programmation et à l’aise avec l’intégration directe à vos bases de données en ligne de commande, je vous recommande de faire appel aux services de certains des professionnels talentueux et actifs ici sur meta.discourse.org.

Encore merci, équipe Discourse !

Note : J’ajouterai quelques leçons apprises dans ce post sur le nouveau site :