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 !!!



